123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- using System;
- using System.Collections.Generic;
- using System.Runtime.CompilerServices;
- using UnityEngine;
- namespace Mirror
- {
- public class NetworkConnectionToClient : NetworkConnection
- {
- // rpcs are collected in a buffer, and then flushed out together.
- // this way we don't need one NetworkMessage per rpc.
- // => prepares for LocalWorldState as well.
- // ensure max size when adding!
- readonly NetworkWriter reliableRpcs = new NetworkWriter();
- readonly NetworkWriter unreliableRpcs = new NetworkWriter();
- public virtual string address => Transport.active.ServerGetClientAddress(connectionId);
- /// <summary>NetworkIdentities that this connection can see</summary>
- // TODO move to server's NetworkConnectionToClient?
- public readonly HashSet<NetworkIdentity> observing = new HashSet<NetworkIdentity>();
- // unbatcher
- public Unbatcher unbatcher = new Unbatcher();
- // server runs a time snapshot interpolation for each client's local time.
- // this is necessary for client auth movement to still be smooth on the
- // server for host mode.
- // TODO move them along server's timeline in the future.
- // perhaps with an offset.
- // for now, keep compatibility by manually constructing a timeline.
- ExponentialMovingAverage driftEma;
- ExponentialMovingAverage deliveryTimeEma; // average delivery time (standard deviation gives average jitter)
- public double remoteTimeline;
- public double remoteTimescale;
- double bufferTimeMultiplier = 2;
- double bufferTime => NetworkServer.sendInterval * bufferTimeMultiplier;
- // <clienttime, snaps>
- readonly SortedList<double, TimeSnapshot> snapshots = new SortedList<double, TimeSnapshot>();
- // Snapshot Buffer size limit to avoid ever growing list memory consumption attacks from clients.
- public int snapshotBufferSizeLimit = 64;
- // ping for rtt (round trip time)
- // useful for statistics, lag compensation, etc.
- double lastPingTime = 0;
- internal ExponentialMovingAverage _rtt = new ExponentialMovingAverage(NetworkTime.PingWindowSize);
- /// <summary>Round trip time (in seconds) that it takes a message to go server->client->server.</summary>
- public double rtt => _rtt.Value;
- public NetworkConnectionToClient(int networkConnectionId)
- : base(networkConnectionId)
- {
- // initialize EMA with 'emaDuration' seconds worth of history.
- // 1 second holds 'sendRate' worth of values.
- // multiplied by emaDuration gives n-seconds.
- driftEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.snapshotSettings.driftEmaDuration);
- deliveryTimeEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.snapshotSettings.deliveryTimeEmaDuration);
- // buffer limit should be at least multiplier to have enough in there
- snapshotBufferSizeLimit = Mathf.Max((int)NetworkClient.snapshotSettings.bufferTimeMultiplier, snapshotBufferSizeLimit);
- }
- public void OnTimeSnapshot(TimeSnapshot snapshot)
- {
- // protect against ever growing buffer size attacks
- if (snapshots.Count >= snapshotBufferSizeLimit) return;
- // (optional) dynamic adjustment
- if (NetworkClient.snapshotSettings.dynamicAdjustment)
- {
- // set bufferTime on the fly.
- // shows in inspector for easier debugging :)
- bufferTimeMultiplier = SnapshotInterpolation.DynamicAdjustment(
- NetworkServer.sendInterval,
- deliveryTimeEma.StandardDeviation,
- NetworkClient.snapshotSettings.dynamicAdjustmentTolerance
- );
- // Debug.Log($"[Server]: {name} delivery std={serverDeliveryTimeEma.StandardDeviation} bufferTimeMult := {bufferTimeMultiplier} ");
- }
- // insert into the server buffer & initialize / adjust / catchup
- SnapshotInterpolation.InsertAndAdjust(
- snapshots,
- NetworkClient.snapshotSettings.bufferLimit,
- snapshot,
- ref remoteTimeline,
- ref remoteTimescale,
- NetworkServer.sendInterval,
- bufferTime,
- NetworkClient.snapshotSettings.catchupSpeed,
- NetworkClient.snapshotSettings.slowdownSpeed,
- ref driftEma,
- NetworkClient.snapshotSettings.catchupNegativeThreshold,
- NetworkClient.snapshotSettings.catchupPositiveThreshold,
- ref deliveryTimeEma
- );
- }
- public void UpdateTimeInterpolation()
- {
- // timeline starts when the first snapshot arrives.
- if (snapshots.Count > 0)
- {
- // progress local timeline.
- SnapshotInterpolation.StepTime(Time.unscaledDeltaTime, ref remoteTimeline, remoteTimescale);
- // progress local interpolation.
- // TimeSnapshot doesn't interpolate anything.
- // this is merely to keep removing older snapshots.
- SnapshotInterpolation.StepInterpolation(snapshots, remoteTimeline, out _, out _, out _);
- // Debug.Log($"NetworkClient SnapshotInterpolation @ {localTimeline:F2} t={t:F2}");
- }
- }
- // Send stage three: hand off to transport
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected override void SendToTransport(ArraySegment<byte> segment, int channelId = Channels.Reliable) =>
- Transport.active.ServerSend(connectionId, segment, channelId);
- protected virtual void UpdatePing()
- {
- // localTime (double) instead of Time.time for accuracy over days
- if (NetworkTime.localTime >= lastPingTime + NetworkTime.PingInterval)
- {
- // TODO it would be safer for the server to store the last N
- // messages' timestamp and only send a message number.
- // This way client's can't just modify the timestamp.
- // predictedTime parameter is 0 because the server doesn't predict.
- NetworkPingMessage pingMessage = new NetworkPingMessage(NetworkTime.localTime, 0);
- Send(pingMessage, Channels.Unreliable);
- lastPingTime = NetworkTime.localTime;
- }
- }
- internal override void Update()
- {
- UpdatePing();
- base.Update();
- }
- /// <summary>Disconnects this connection.</summary>
- public override void Disconnect()
- {
- // set not ready and handle clientscene disconnect in any case
- // (might be client or host mode here)
- isReady = false;
- reliableRpcs.Position = 0;
- unreliableRpcs.Position = 0;
- Transport.active.ServerDisconnect(connectionId);
- // IMPORTANT: NetworkConnection.Disconnect() is NOT called for
- // voluntary disconnects from the other end.
- // -> so all 'on disconnect' cleanup code needs to be in
- // OnTransportDisconnect, where it's called for both voluntary
- // and involuntary disconnects!
- }
- internal void AddToObserving(NetworkIdentity netIdentity)
- {
- observing.Add(netIdentity);
- // spawn identity for this conn
- NetworkServer.ShowForConnection(netIdentity, this);
- }
- internal void RemoveFromObserving(NetworkIdentity netIdentity, bool isDestroyed)
- {
- observing.Remove(netIdentity);
- if (!isDestroyed)
- {
- // hide identity for this conn
- NetworkServer.HideForConnection(netIdentity, this);
- }
- }
- internal void RemoveFromObservingsObservers()
- {
- foreach (NetworkIdentity netIdentity in observing)
- {
- netIdentity.RemoveObserver(this);
- }
- observing.Clear();
- }
- internal void AddOwnedObject(NetworkIdentity obj)
- {
- owned.Add(obj);
- }
- internal void RemoveOwnedObject(NetworkIdentity obj)
- {
- owned.Remove(obj);
- }
- internal void DestroyOwnedObjects()
- {
- // create a copy because the list might be modified when destroying
- HashSet<NetworkIdentity> tmp = new HashSet<NetworkIdentity>(owned);
- foreach (NetworkIdentity netIdentity in tmp)
- {
- if (netIdentity != null)
- {
- // unspawn scene objects, destroy instantiated objects.
- // fixes: https://github.com/MirrorNetworking/Mirror/issues/3538
- if (netIdentity.sceneId != 0)
- NetworkServer.UnSpawn(netIdentity.gameObject);
- else
- NetworkServer.Destroy(netIdentity.gameObject);
- }
- }
- // clear the hashset because we destroyed them all
- owned.Clear();
- }
- }
- }
|