This repository has been archived on 2023-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
station_obscurum_unity/Assets/FishNet/Runtime/Connection/NetworkConnection.cs

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);
}
}
}