404 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			404 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using FishNet.Documenting;
 | |
| using FishNet.Managing;
 | |
| using FishNet.Object;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Runtime.CompilerServices;
 | |
| using UnityEngine.SceneManagement;
 | |
| 
 | |
| namespace FishNet.Connection
 | |
| {
 | |
| 
 | |
|     /// <summary>
 | |
|     /// A container for a connected client used to perform actions on and gather information for the declared client.
 | |
|     /// </summary>
 | |
|     public partial class NetworkConnection : IEquatable<NetworkConnection>
 | |
|     {
 | |
| 
 | |
|         #region Public.
 | |
|         /// <summary>
 | |
|         /// Called after this connection has loaded start scenes. Boolean will be true if asServer. Available to this connection and server.
 | |
|         /// </summary>
 | |
|         public event Action<NetworkConnection, bool> OnLoadedStartScenes;
 | |
|         /// <summary>
 | |
|         /// Called after connection gains ownership of an object, and after the object has been added to Objects. Available to this connection and server.
 | |
|         /// </summary>
 | |
|         public event Action<NetworkObject> OnObjectAdded;
 | |
|         /// <summary>
 | |
|         /// Called after connection loses ownership of an object, and after the object has been removed from Objects. Available to this connection and server.
 | |
|         /// </summary>
 | |
|         public event Action<NetworkObject> OnObjectRemoved;
 | |
|         /// <summary>
 | |
|         /// NetworkManager managing this class.
 | |
|         /// </summary>
 | |
|         public NetworkManager NetworkManager { get; private set; }
 | |
|         /// <summary>
 | |
|         /// True if connection has loaded start scenes. Available to this connection and server.
 | |
|         /// </summary>
 | |
|         public bool LoadedStartScenes() => (_loadedStartScenesAsServer || _loadedStartScenesAsClient);
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         public bool LoadedStartScenes(bool asServer)
 | |
|         {
 | |
|             if (asServer)
 | |
|                 return _loadedStartScenesAsServer;
 | |
|             else
 | |
|                 return _loadedStartScenesAsClient;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// True if loaded start scenes as server.
 | |
|         /// </summary>
 | |
|         private bool _loadedStartScenesAsServer;
 | |
|         /// <summary>
 | |
|         /// True if loaded start scenes as client.
 | |
|         /// </summary>
 | |
|         private bool _loadedStartScenesAsClient;
 | |
|         /// <summary>
 | |
|         /// ObjectIds to use for predicted spawning.
 | |
|         /// </summary>
 | |
|         internal Queue<int> PredictedObjectIds = new Queue<int>();
 | |
|         /// <summary>
 | |
|         /// True if this connection is authenticated. Only available to server.
 | |
|         /// </summary>
 | |
|         public bool Authenticated { get; private set; }
 | |
|         /// <summary>
 | |
|         /// True if this connection IsValid and not Disconnecting.
 | |
|         /// </summary>
 | |
|         public bool IsActive => (ClientId >= 0 && !Disconnecting);
 | |
|         /// <summary>
 | |
|         /// True if this connection is valid. An invalid connection indicates no client is set for this reference.
 | |
|         /// </summary>
 | |
|         public bool IsValid => (ClientId >= 0);
 | |
|         /// <summary>
 | |
|         /// Unique Id for this connection.
 | |
|         /// </summary>
 | |
|         public int ClientId = -1;
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         private HashSet<NetworkObject> _objects = new HashSet<NetworkObject>();
 | |
|         /// <summary>
 | |
|         /// Objects owned by this connection. Available to this connection and server.
 | |
|         /// </summary>
 | |
|         public IReadOnlyCollection<NetworkObject> Objects => _objects;
 | |
|         /// <summary>
 | |
|         /// The first object within Objects.
 | |
|         /// </summary>
 | |
|         public NetworkObject FirstObject { get; private set; }
 | |
|         /// <summary>
 | |
|         /// Scenes this connection is in. Available to this connection and server.
 | |
|         /// </summary>
 | |
|         public HashSet<Scene> Scenes { get; private set; } = new HashSet<Scene>();
 | |
|         /// <summary>
 | |
|         /// True if this connection is being disconnected. Only available to server.
 | |
|         /// </summary>
 | |
|         public bool Disconnecting { get; private set; }
 | |
|         /// <summary>
 | |
|         /// Tick when Disconnecting was set.
 | |
|         /// </summary>
 | |
|         internal uint DisconnectingTick { get; private set; }
 | |
|         /// <summary>
 | |
|         /// Custom data associated with this connection which may be modified by the user.
 | |
|         /// The value of this field are not synchronized over the network.
 | |
|         /// </summary>
 | |
|         public object CustomData = null;
 | |
|         /// <summary>
 | |
|         /// Local tick when the connection last replicated.
 | |
|         /// </summary>
 | |
|         public uint LocalReplicateTick { get; internal set; }
 | |
|         /// <summary>
 | |
|         /// Tick of the last packet received from this connection.
 | |
|         /// This value is only available on the server.
 | |
|         /// </summary>
 | |
|         /* This is not used internally. At this time it's just
 | |
|          * here for the users convienence. */
 | |
|         public uint LastPacketTick { get; private set; }
 | |
|         /// <summary>
 | |
|         /// Sets LastPacketTick value.
 | |
|         /// </summary>
 | |
|         /// <param name="value"></param>
 | |
|         internal void SetLastPacketTick(uint value)
 | |
|         {
 | |
|             //If new largest tick from the client then update client tick data.
 | |
|             if (value > LastPacketTick)
 | |
|             {
 | |
|                 _latestTick = value;
 | |
|                 _serverLatestTick = NetworkManager.TimeManager.Tick;
 | |
|             }
 | |
|             LastPacketTick = value;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Latest tick that did not arrive out of order from this connection.
 | |
|         /// </summary>
 | |
|         private uint _latestTick;
 | |
|         /// <summary>
 | |
|         /// Tick on the server when latestTick was set.
 | |
|         /// </summary>
 | |
|         private uint _serverLatestTick;
 | |
|         [Obsolete("Use LocalTick instead.")] //Remove on 2023/06/01
 | |
|         public uint Tick => LocalTick;
 | |
|         /// <summary>
 | |
|         /// Current approximate local tick as it is on this connection.
 | |
|         /// </summary>
 | |
|         public uint LocalTick
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 NetworkManager nm = NetworkManager;
 | |
|                 if (nm != null)
 | |
|                 {
 | |
|                     uint diff = (nm.TimeManager.Tick - _serverLatestTick);
 | |
|                     return (diff + _latestTick);
 | |
|                 }
 | |
| 
 | |
|                 //Fall through, could not process.
 | |
|                 return 0;
 | |
|             }
 | |
| 
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         #region Const.
 | |
|         /// <summary>
 | |
|         /// Value used when ClientId has not been set.
 | |
|         /// </summary>
 | |
|         public const int UNSET_CLIENTID_VALUE = -1;
 | |
|         #endregion
 | |
| 
 | |
|         #region Comparers.
 | |
|         public override bool Equals(object obj)
 | |
|         {
 | |
|             if (obj is NetworkConnection nc)
 | |
|                 return (nc.ClientId == this.ClientId);
 | |
|             else
 | |
|                 return false;
 | |
|         }
 | |
|         public bool Equals(NetworkConnection nc)
 | |
|         {
 | |
|             if (nc is null)
 | |
|                 return false;
 | |
|             //If either is -1 Id.
 | |
|             if (this.ClientId == NetworkConnection.UNSET_CLIENTID_VALUE || nc.ClientId == NetworkConnection.UNSET_CLIENTID_VALUE)
 | |
|                 return false;
 | |
|             //Same object.
 | |
|             if (System.Object.ReferenceEquals(this, nc))
 | |
|                 return true;
 | |
| 
 | |
|             return (this.ClientId == nc.ClientId);
 | |
|         }
 | |
|         public override int GetHashCode()
 | |
|         {
 | |
|             return ClientId;
 | |
|         }
 | |
|         public static bool operator ==(NetworkConnection a, NetworkConnection b)
 | |
|         {
 | |
|             if (a is null && b is null)
 | |
|                 return true;
 | |
|             if (a is null && !(b is null))
 | |
|                 return false;
 | |
| 
 | |
|             return (b == null) ? a.Equals(b) : b.Equals(a);
 | |
|         }
 | |
|         public static bool operator !=(NetworkConnection a, NetworkConnection b)
 | |
|         {
 | |
|             return !(a == b);
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         [APIExclude]
 | |
|         public NetworkConnection() { }
 | |
|         [APIExclude]
 | |
|         public NetworkConnection(NetworkManager manager, int clientId, bool asServer)
 | |
|         {
 | |
|             Initialize(manager, clientId, asServer);
 | |
|         }
 | |
| 
 | |
|         public void Dispose()
 | |
|         {
 | |
|             foreach (PacketBundle p in _toClientBundles)
 | |
|                 p.Dispose();
 | |
|             _toClientBundles.Clear();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes this for use.
 | |
|         /// </summary>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         private void Initialize(NetworkManager nm, int clientId, bool asServer)
 | |
|         {
 | |
|             NetworkManager = nm;
 | |
|             ClientId = clientId;
 | |
|             //Only the server uses the ping and buffer.
 | |
|             if (asServer)
 | |
|             {
 | |
|                 InitializeBuffer();
 | |
|                 InitializePing();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Resets this instance.
 | |
|         /// </summary>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         internal void Reset()
 | |
|         {
 | |
|             _latestTick = 0;
 | |
|             _serverLatestTick = 0;
 | |
|             LastPacketTick = 0;
 | |
|             ClientId = -1;
 | |
|             ClearObjects();
 | |
|             Authenticated = false;
 | |
|             NetworkManager = null;
 | |
|             _loadedStartScenesAsClient = false;
 | |
|             _loadedStartScenesAsServer = false;
 | |
|             SetDisconnecting(false);
 | |
|             Scenes.Clear();
 | |
|             PredictedObjectIds.Clear();
 | |
|             ResetPingPong();
 | |
|             LevelOfDetails.Clear();
 | |
|             AllowedForcedLodUpdates = 0;
 | |
|             LastLevelOfDetailUpdate = 0;
 | |
|             LevelOfDetailInfractions = 0;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets Disconnecting boolean for this connection.
 | |
|         /// </summary>
 | |
|         internal void SetDisconnecting(bool value)
 | |
|         {
 | |
|             Disconnecting = value;
 | |
|             if (Disconnecting)
 | |
|                 DisconnectingTick = NetworkManager.TimeManager.LocalTick;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Disconnects this connection.
 | |
|         /// </summary>
 | |
|         /// <param name="immediately">True to disconnect immediately. False to send any pending data first.</param>
 | |
|         public void Disconnect(bool immediately)
 | |
|         {
 | |
|             if (Disconnecting)
 | |
|             {
 | |
|                 NetworkManager.LogWarning($"ClientId {ClientId} is already disconnecting.");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             SetDisconnecting(true);
 | |
|             //If immediately then force disconnect through transport.
 | |
|             if (immediately)
 | |
|                 NetworkManager.TransportManager.Transport.StopConnection(ClientId, true);
 | |
|             //Otherwise mark dirty so server will push out any pending information, and then disconnect.
 | |
|             else
 | |
|                 ServerDirty();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if just loaded start scenes and sets them as loaded if not.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         internal bool SetLoadedStartScenes(bool asServer)
 | |
|         {
 | |
|             bool loadedToCheck = (asServer) ? _loadedStartScenesAsServer : _loadedStartScenesAsClient;
 | |
|             //Result becomes true if not yet loaded start scenes.
 | |
|             bool result = !loadedToCheck;
 | |
|             if (asServer)
 | |
|                 _loadedStartScenesAsServer = true;
 | |
|             else
 | |
|                 _loadedStartScenesAsClient = true;
 | |
| 
 | |
|             OnLoadedStartScenes?.Invoke(this, asServer);
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets connection as authenticated.
 | |
|         /// </summary>
 | |
|         internal void ConnectionAuthenticated()
 | |
|         {
 | |
|             Authenticated = true;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Adds to Objects owned by this connection.
 | |
|         /// </summary>
 | |
|         /// <param name="nob"></param>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         internal void AddObject(NetworkObject nob)
 | |
|         {
 | |
|             _objects.Add(nob);
 | |
|             //If adding the first object then set new FirstObject.
 | |
|             if (_objects.Count == 1)
 | |
|                 FirstObject = nob;
 | |
| 
 | |
|             OnObjectAdded?.Invoke(nob);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Removes from Objects owned by this connection.
 | |
|         /// </summary>
 | |
|         /// <param name="nob"></param>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         internal void RemoveObject(NetworkObject nob)
 | |
|         {
 | |
|             _objects.Remove(nob);
 | |
|             //If removing the first object then set a new one.
 | |
|             if (nob == FirstObject)
 | |
|                 SetFirstObject();
 | |
| 
 | |
|             OnObjectRemoved?.Invoke(nob);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Clears all Objects.
 | |
|         /// </summary>
 | |
|         private void ClearObjects()
 | |
|         {
 | |
|             _objects.Clear();
 | |
|             FirstObject = null;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets FirstObject using the first element in Objects.
 | |
|         /// </summary>
 | |
|         private void SetFirstObject()
 | |
|         {
 | |
|             if (_objects.Count == 0)
 | |
|             {
 | |
|                 FirstObject = null;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 foreach (NetworkObject nob in Objects)
 | |
|                 {
 | |
|                     FirstObject = nob;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Adds a scene to this connections Scenes.
 | |
|         /// </summary>
 | |
|         /// <param name="scene"></param>
 | |
|         /// <returns></returns>
 | |
|         internal bool AddToScene(Scene scene)
 | |
|         {
 | |
|             return Scenes.Add(scene);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Removes a scene to this connections Scenes.
 | |
|         /// </summary>
 | |
|         /// <param name="scene"></param>
 | |
|         /// <returns></returns>
 | |
|         internal bool RemoveFromScene(Scene scene)
 | |
|         {
 | |
|             return Scenes.Remove(scene);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
| } |