Moved a couple of folders and wrote some code

This commit is contained in:
Madhav Kapa
2023-06-01 11:21:49 -07:00
parent 0eea70ab4e
commit e8684391ca
1380 changed files with 2766 additions and 13987 deletions

View File

@ -0,0 +1,19 @@
namespace FishNet.Transporting
{
/// <summary>
/// Channel which data is sent or received.
/// </summary>
public enum Channel : byte
{
/// <summary>
/// Data will be sent ordered reliable.
/// </summary>
Reliable = 0,
/// <summary>
/// Data will be sent unreliable.
/// </summary>
Unreliable = 1
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7cd503d67a974984385164c53bd3e518
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
namespace FishNet.Transporting
{
/// <summary>
/// States the local connection can be in.
/// </summary>
public enum LocalConnectionState : byte
{
/// <summary>
/// Connection is fully stopped.
/// </summary>
Stopped = 0,
/// <summary>
/// Connection is starting but not yet established.
/// </summary>
Starting = 1,
/// <summary>
/// Connection is established.
/// </summary>
Started = 2,
/// <summary>
/// Connection is stopping.
/// </summary>
Stopping = 3
}
/// <summary>
/// States a remote client can be in.
/// </summary>
public enum RemoteConnectionState : byte
{
/// <summary>
/// Connection is fully stopped.
/// </summary>
Stopped = 0,
/// <summary>
/// Connection is established.
/// </summary>
Started = 2,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 45640e2b3919981499b359ecc2154d3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,152 @@
using System;
namespace FishNet.Transporting
{
/// <summary>
/// Container about data received on the server.
/// </summary>
public struct ServerReceivedDataArgs
{
/// <summary>
/// Data received.
/// </summary>
public ArraySegment<byte> Data;
/// <summary>
/// Channel data was received on.
/// </summary>
public Channel Channel;
/// <summary>
/// ConnectionId from which client sent data, if data was received on the server.
/// </summary>
public int ConnectionId;
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
/// <summary>
/// Delegate to invoke after data is processed.
/// </summary>
/// <returns></returns>
public Action FinalizeMethod;
public ServerReceivedDataArgs(ArraySegment<byte> data, Channel channel, int connectionId, int transportIndex)
{
Data = data;
Channel = channel;
ConnectionId = connectionId;
TransportIndex = transportIndex;
FinalizeMethod = null;
}
public ServerReceivedDataArgs(ArraySegment<byte> data, Channel channel, int connectionId, int transportIndex, Action finalizeMethod)
{
Data = data;
Channel = channel;
ConnectionId = connectionId;
TransportIndex = transportIndex;
FinalizeMethod = finalizeMethod;
}
}
/// <summary>
/// Container about data received on the local client.
/// </summary>
public struct ClientReceivedDataArgs
{
/// <summary>
/// Data received.
/// </summary>
public ArraySegment<byte> Data;
/// <summary>
/// Channel data was received on.
/// </summary>
public Channel Channel;
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
public ClientReceivedDataArgs(ArraySegment<byte> data, Channel channel, int transportIndex)
{
Data = data;
Channel = channel;
TransportIndex = transportIndex;
}
}
/// <summary>
/// Container about a connection state change for a client.
/// </summary>
public struct RemoteConnectionStateArgs
{
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
/// <summary>
/// New connection state.
/// </summary>
public RemoteConnectionState ConnectionState;
/// <summary>
/// ConnectionId for which client the state changed. Will be -1 if ConnectionState was for the local server.
/// </summary>
public int ConnectionId;
public RemoteConnectionStateArgs(RemoteConnectionState connectionState, int connectionId, int transportIndex)
{
ConnectionState = connectionState;
ConnectionId = connectionId;
TransportIndex = transportIndex;
}
}
/// <summary>
/// Container about a connection state change for the server.
/// </summary>
public struct ServerConnectionStateArgs
{
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
/// <summary>
/// New connection state.
/// </summary>
public LocalConnectionState ConnectionState;
public ServerConnectionStateArgs(LocalConnectionState connectionState, int transportIndex)
{
ConnectionState = connectionState;
TransportIndex = transportIndex;
}
}
/// <summary>
/// Container about a connection state change for the local client.
/// </summary>
public struct ClientConnectionStateArgs
{
/// <summary>
/// New connection state.
/// </summary>
public LocalConnectionState ConnectionState;
/// <summary>
/// Index of the transport that is for.
/// This is primarily used when supporting multiple transports.
/// </summary>
public int TransportIndex;
public ClientConnectionStateArgs(LocalConnectionState connectionState, int transportIndex)
{
ConnectionState = connectionState;
TransportIndex = transportIndex;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 718b9d27800e70848b50b2c7b0117e5c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
namespace FishNet.Transporting
{
/// <summary>
/// Channel which data is sent or received.
/// </summary>
public enum IPAddressType : byte
{
/// <summary>
/// Address is IPv4.
/// </summary>
IPv4 = 0,
/// <summary>
/// Address is IPv6.
/// </summary>
IPv6 = 1
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11a2c7610ce4ce34a915683bd4607714
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
using FishNet.Managing.Timing;
using UnityEngine;
namespace FishNet.Transporting
{
[DisallowMultipleComponent]
[DefaultExecutionOrder(short.MinValue)]
internal class NetworkReaderLoop : MonoBehaviour
{
#region Private.
/// <summary>
/// TimeManager this loop is for.
/// </summary>
private TimeManager _timeManager;
#endregion
private void Awake()
{
_timeManager = GetComponent<TimeManager>();
}
private void FixedUpdate()
{
_timeManager.TickFixedUpdate();
}
private void Update()
{
_timeManager.TickUpdate();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d359d6ef33641f41a2ae67d1abdfdd3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using FishNet.Managing.Timing;
using UnityEngine;
namespace FishNet.Transporting
{
[DisallowMultipleComponent]
[DefaultExecutionOrder(short.MaxValue)]
internal class NetworkWriterLoop : MonoBehaviour
{
#region Private.
/// <summary>
/// TimeManager this loop is for.
/// </summary>
private TimeManager _timeManager;
#endregion
private void Awake()
{
_timeManager = GetComponent<TimeManager>();
}
private void LateUpdate()
{
Iterate();
}
/// <summary>
/// Performs read on transport.
/// </summary>
private void Iterate()
{
_timeManager.TickLateUpdate();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fed05d526ab23949bac6cd2bf041c35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
using FishNet.Documenting;
namespace FishNet.Transporting
{
/// <summary>
/// PacketIds to indicate the type of packet which is being sent or arriving.
/// </summary>
[APIExclude]
public enum PacketId : ushort
{
Unset = 0,
Authenticated = 1,
Split = 2,
ObjectSpawn = 3,
ObjectDespawn = 4,
PredictedSpawnResult = 5,
SyncVar = 7,
ServerRpc = 8,
ObserversRpc = 9,
TargetRpc = 10,
OwnershipChange = 11,
Broadcast = 12,
SyncObject = 13,
PingPong = 14,
Replicate = 15,
Reconcile = 16,
Disconnect = 17,
TimingUpdate = 18,
NetworkLODUpdate = 19,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f3b7256982245b46a2925e2b94ce149
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,243 @@
using FishNet.Managing;
using FishNet.Managing.Logging;
using System;
using UnityEngine;
namespace FishNet.Transporting
{
/// <summary>
/// Processes connection states, and data sent to and from a socket.
/// </summary>
public abstract class Transport : MonoBehaviour
{
#region Private.
/// <summary>
/// NetworkManager for this transport.
/// </summary>
public NetworkManager NetworkManager { get; private set; }
/// <summary>
/// Index this transport belongs to when using multiple transports at once.
/// </summary>
public int Index { get; private set; }
#endregion
#region Initialization and unity.
/// <summary>
/// Initializes the transport. Use this instead of Awake.
/// <param name="transportIndex">Index this transport belongs to when using multiple transports at once.</param>
/// </summary>
public virtual void Initialize(NetworkManager networkManager, int transportIndex)
{
NetworkManager = networkManager;
Index = transportIndex;
}
#endregion
#region ConnectionStates.
/// <summary>
/// Gets the address of a remote connection Id.
/// </summary>
/// <param name="connectionId">Connectionid to get the address for.</param>
/// <returns></returns>
public abstract string GetConnectionAddress(int connectionId);
/// <summary>
/// Called when a connection state changes for the local client.
/// </summary>
public abstract event Action<ClientConnectionStateArgs> OnClientConnectionState;
/// <summary>
/// Called when a connection state changes for the local server.
/// </summary>
public abstract event Action<ServerConnectionStateArgs> OnServerConnectionState;
/// <summary>
/// Called when a connection state changes for a remote client.
/// </summary>
public abstract event Action<RemoteConnectionStateArgs> OnRemoteConnectionState;
/// <summary>
/// Handles a ConnectionStateArgs for the local client.
/// </summary>
/// <param name="connectionStateArgs">Data being handled.</param>
public abstract void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs);
/// <summary>
/// Handles a ConnectionStateArgs for the local server.
/// </summary>
/// <param name="connectionStateArgs">Data being handled.</param>
public abstract void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs);
/// <summary>
/// Handles a ConnectionStateArgs for a remote client.
/// </summary>
/// <param name="connectionStateArgs">Data being handled.</param>
public abstract void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs);
/// <summary>
/// Gets the current local ConnectionState.
/// </summary>
/// <param name="server">True if getting ConnectionState for the server.</param>
public abstract LocalConnectionState GetConnectionState(bool server);
/// <summary>
/// Gets the current ConnectionState of a client connected to the server. Can only be called on the server.
/// </summary>
/// <param name="connectionId">ConnectionId to get ConnectionState for.</param>
public abstract RemoteConnectionState GetConnectionState(int connectionId);
#endregion
#region Sending.
/// <summary>
/// Sends to the server.
/// </summary>
/// <param name="channelId">Channel to use.</param>
/// <param name="segment">Data to send.</param>
public abstract void SendToServer(byte channelId, ArraySegment<byte> segment);
/// <summary>
/// Sends to a client.
/// </summary>
/// <param name="channelId">Channel to use.</param>
/// <param name="segment">Data to send.</param>
/// <param name="connectionId">ConnectionId to send to. When sending to clients can be used to specify which connection to send to.</param>
public abstract void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId);
#endregion
#region Receiving
/// <summary>
/// Called when the client receives data.
/// </summary>
public abstract event Action<ClientReceivedDataArgs> OnClientReceivedData;
/// <summary>
/// Handles a ClientReceivedDataArgs.
/// </summary>
/// <param name="receivedDataArgs">Data being handled.</param>
public abstract void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs);
/// <summary>
/// Called when the server receives data.
/// </summary>
public abstract event Action<ServerReceivedDataArgs> OnServerReceivedData;
/// <summary>
/// Handles a ServerReceivedDataArgs.
/// </summary>
/// <param name="receivedDataArgs">Data being handled.</param>
public abstract void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs);
#endregion
#region Iterating.
/// <summary>
/// Processes data received by the socket.
/// </summary>
/// <param name="server">True to process data received on the server.</param>
public abstract void IterateIncoming(bool server);
/// <summary>
/// Processes data to be sent by the socket.
/// </summary>
/// <param name="server">True to process data received on the server.</param>
public abstract void IterateOutgoing(bool server);
#endregion
#region Configuration.
/// <summary>
/// Returns if the transport is only run locally, offline.
/// While true several security checks are disabled.
/// </summary>
/// <param name="connectionid"></param>
public virtual bool IsLocalTransport(int connectionid) => false;
/// <summary>
/// Gets how long in seconds until either the server or client socket must go without data before being timed out.
/// </summary>
/// <param name="asServer">True to get the timeout for the server socket, false for the client socket.</param>
/// <returns></returns>
public virtual float GetTimeout(bool asServer) => -1f;
/// <summary>
/// Sets how long in seconds until either the server or client socket must go without data before being timed out.
/// </summary>
/// <param name="asServer">True to set the timeout for the server socket, false for the client socket.</param>
public virtual void SetTimeout(float value, bool asServer) { }
/// <summary>
/// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned.
/// </summary>
/// <returns>Maximum clients transport allows.</returns>
public virtual int GetMaximumClients()
{
string message = $"The current transport does not support this feature.";
if (NetworkManager == null)
NetworkManager.StaticLogWarning(message);
else
NetworkManager.LogWarning(message);
return -1;
}
/// <summary>
/// Sets the maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect.
/// </summary>
/// <param name="value">Maximum clients to allow.</param>
public virtual void SetMaximumClients(int value)
{
string message = $"The current transport does not support this feature.";
if (NetworkManager == null)
NetworkManager.StaticLogWarning(message);
else
NetworkManager.LogWarning(message);
}
/// <summary>
/// Sets which address the client will connect to.
/// </summary>
/// <param name="address">Address client will connect to.</param>
public virtual void SetClientAddress(string address) { }
/// <summary>
/// Returns which address the client will connect to.
/// </summary>
public virtual string GetClientAddress() => string.Empty;
/// <summary>
/// Sets which address the server will bind to.
/// </summary>
/// <param name="address">Address server will bind to.</param>
/// <param name="addressType">Address type to set.</param>
public virtual void SetServerBindAddress(string address, IPAddressType addressType) { }
/// <summary>
/// Gets which address the server will bind to.
/// </summary>
/// <param name="addressType">Address type to return.</param>
public virtual string GetServerBindAddress(IPAddressType addressType) => string.Empty;
/// <summary>
/// Sets which port to use.
/// </summary>
/// <param name="port">Port to use.</param>
public virtual void SetPort(ushort port) { }
/// <summary>
/// Gets which port to use.
/// </summary>
public virtual ushort GetPort() => 0;
#endregion
#region Start and stop.
/// <summary>
/// Starts the local server or client using configured settings.
/// </summary>
/// <param name="server">True to start server.</param>
public abstract bool StartConnection(bool server);
/// <summary>
/// Stops the local server or client.
/// </summary>
/// <param name="server">True to stop server.</param>
public abstract bool StopConnection(bool server);
/// <summary>
/// Stops a remote client from the server, disconnecting the client.
/// </summary>
/// <param name="connectionId">ConnectionId of the client to disconnect.</param>
/// <param name="immediately">True to abrutly stop the client socket. The technique used to accomplish immediate disconnects may vary depending on the transport.
/// When not using immediate disconnects it's recommended to perform disconnects using the ServerManager rather than accessing the transport directly.
/// </param>
public abstract bool StopConnection(int connectionId, bool immediately);
/// <summary>
/// Stops both client and server.
/// </summary>
public abstract void Shutdown();
#endregion
#region Channels.
/// <summary>
/// Gets the MTU for a channel.
/// </summary>
/// <param name="channel">Channel to get MTU for.</param>
/// <returns>MTU of channel.</returns>
public abstract int GetMTU(byte channel);
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78aba14618b37ea4bb067fa95ede84e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb8b443596223e84aaca2634238adda3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7f35b43a13fceaa40ac25cef58d8e53b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,2 @@
1.0.0
- Initial release.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9b339dd67a0ce7f458236a3ad1d97322
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,946 @@
using FishNet.Managing;
using FishNet.Utility.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Transporting.Multipass
{
[AddComponentMenu("FishNet/Transport/Multipass")]
public class Multipass : Transport
{
#region Types.
public struct TransportIdData
{
public int TransportId;
public int TransportIndex;
public TransportIdData(int transportId, int transportIndex)
{
TransportId = transportId;
TransportIndex = transportIndex;
}
}
#endregion
#region Public.
/// <summary>
/// While true server actions such as starting or stopping the server will run on all transport.
/// </summary>
[Tooltip("While true server actions such as starting or stopping the server will run on all transport.")]
public bool GlobalServerActions = true;
/// <summary>
///
/// </summary>
private Transport _clientTransport;
/// <summary>
/// Transport the client is using.
/// Use SetClientTransport to assign this value.
/// </summary>
[HideInInspector]
public Transport ClientTransport
{
get
{
//If not yet set.
if (_clientTransport == null)
{
//If there are transports to set from.
if (_transports.Count != 0)
_clientTransport = _transports[0];
/* Give feedback to developer that transport was not set
* before accessing this. Transport should always be set
* manually rather than assuming the default client
* transport. */
if (_clientTransport == null)
base.NetworkManager.LogError($"ClientTransport in Multipass could not be set to the first transport. This can occur if no trnasports are specified or if the first entry is null.");
else
base.NetworkManager.LogError($"ClientTransport in Multipass is being automatically set to {_clientTransport.GetType()}. For production use SetClientTransport before attempting to access the ClientTransport.");
}
return _clientTransport;
}
private set => _clientTransport = value;
}
#endregion
#region Serialized.
/// <summary>
///
/// </summary>
[Tooltip("Transports to use.")]
[SerializeField]
private List<Transport> _transports = new List<Transport>();
/// <summary>
/// Transports to use.
/// </summary>
public IList<Transport> Transports => _transports;
#endregion
#region Private.
/// <summary>
/// Key is the transport connectionid, Value is the TransportIdData.
/// </summary>
private Dictionary<int, TransportIdData> _multipassToTransport = new Dictionary<int, TransportIdData>();
/// <summary>
/// Key is the Multipass connectionId, Value is the transport connectionId.
/// </summary>
private List<Dictionary<int, int>> _transportToMultipass = new List<Dictionary<int, int>>();
/// <summary>
/// Ids available to new connections.
/// </summary>
private Queue<int> _availableIds = new Queue<int>();
#endregion
#region Const.
/// <summary>
/// Id to use for client when acting as host.
/// </summary>
internal const int CLIENT_HOST_ID = short.MaxValue;
#endregion
public override void Initialize(NetworkManager networkManager, int transportIndex)
{
base.Initialize(networkManager, transportIndex);
//Remove any null transports and warn.
for (int i = 0; i < _transports.Count; i++)
{
if (_transports[i] == null)
{
base.NetworkManager.LogWarning($"Transports contains a null entry on index {i}.");
_transports.RemoveAt(i);
i--;
}
}
//No transports to use.
if (_transports.Count == 0)
{
base.NetworkManager.LogError($"No transports are set within Multipass.");
return;
}
//Create transportsToMultipass.
for (int i = 0; i < _transports.Count; i++)
{
Dictionary<int, int> dict = new Dictionary<int, int>();
_transportToMultipass.Add(dict);
}
//Initialize each transport.
for (int i = 0; i < _transports.Count; i++)
{
_transports[i].Initialize(networkManager, i);
_transports[i].OnClientConnectionState += Multipass_OnClientConnectionState;
_transports[i].OnServerConnectionState += Multipass_OnServerConnectionState;
_transports[i].OnRemoteConnectionState += Multipass_OnRemoteConnectionState;
_transports[i].OnClientReceivedData += Multipass_OnClientReceivedData;
_transports[i].OnServerReceivedData += Multipass_OnServerReceivedData;
}
}
private void OnDestroy()
{
//Initialize each transport.
foreach (Transport t in _transports)
t.Shutdown();
}
#region ClientIds.
/// <summary>
/// Clears ClientIds when appropriate.
/// </summary>
private void TryResetClientIds(bool force)
{
//Can only clear when every transport server isnt connected.
if (!force)
{
foreach (Transport t in _transports)
{
//Cannot clear if a server is running still.
if (t.GetConnectionState(true) == LocalConnectionState.Started)
return;
}
}
_multipassToTransport.Clear();
foreach (Dictionary<int, int> item in _transportToMultipass)
item.Clear();
CreateAvailableIds();
}
/// <summary>
/// Gets the Multipass connectionId using a transport connectionid.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool GetMultipassId(int transportIndex, int transportId, out int multipassId)
{
Dictionary<int, int> dict = _transportToMultipass[transportIndex];
if (!dict.TryGetValueIL2CPP(transportId, out multipassId))
{
multipassId = -1;
base.NetworkManager.LogError($"Multipass connectionId could not be found for transportIndex {transportIndex}, transportId of {transportId}.");
return false;
}
return true;
}
/// <summary>
/// Gets the TransportIdData using a Multipass connectionId.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool GetTransportIdData(int multipassId, out TransportIdData data)
{
if (!_multipassToTransport.TryGetValueIL2CPP(multipassId, out data))
{
//Fall through.
base.NetworkManager.LogError($"TransportIdData could not be found for Multipass connectionId of {multipassId}.");
return false;
}
return true;
}
#endregion
#region ConnectionStates.
/// <summary>
/// Gets the IP address of a remote connectionId.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string GetConnectionAddress(int connectionId)
{
TransportIdData data;
if (!GetTransportIdData(connectionId, out data))
return string.Empty;
return _transports[data.TransportIndex].GetConnectionAddress(data.TransportId);
}
/// <summary>
/// Called when a connection state changes for the local client.
/// </summary>
public override event Action<ClientConnectionStateArgs> OnClientConnectionState;
/// <summary>
/// Called when a connection state changes for the local server.
/// </summary>
public override event Action<ServerConnectionStateArgs> OnServerConnectionState;
/// <summary>
/// Called when a connection state changes for a remote client.
/// </summary>
public override event Action<RemoteConnectionStateArgs> OnRemoteConnectionState;
/// <summary>
/// Gets the current local ConnectionState of the first transport.
/// </summary>
/// <param name="server">True if getting ConnectionState for the server.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override LocalConnectionState GetConnectionState(bool server)
{
if (server)
{
base.NetworkManager.LogError($"This method is not supported for server. Use GetConnectionState(server, transportIndex) instead.");
return LocalConnectionState.Stopped;
}
if (IsClientTransportSetWithError("GetConnectionState"))
return GetConnectionState(server, ClientTransport.Index);
else
return LocalConnectionState.Stopped;
}
/// <summary>
/// Gets the current local ConnectionState of the transport on index.
/// </summary>
/// <param name="server">True if getting ConnectionState for the server.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public LocalConnectionState GetConnectionState(bool server, int index)
{
if (!IndexInRange(index, true))
return LocalConnectionState.Stopped;
return _transports[index].GetConnectionState(server);
}
/// <summary>
/// Gets the current ConnectionState of a remote client on the server.
/// </summary>
/// <param name="connectionId">ConnectionId to get ConnectionState for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override RemoteConnectionState GetConnectionState(int connectionId)
{
TransportIdData data;
if (!GetTransportIdData(connectionId, out data))
return RemoteConnectionState.Stopped;
return _transports[data.TransportIndex].GetConnectionState(data.TransportId);
}
/// <summary>
/// Gets the current ConnectionState of a remote client on the server of the transport on index.
/// </summary>
/// <param name="connectionId">ConnectionId to get ConnectionState for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RemoteConnectionState GetConnectionState(int connectionId, int index)
{
if (!IndexInRange(index, true))
return RemoteConnectionState.Stopped;
return _transports[index].GetConnectionState(connectionId);
}
/// <summary>
/// Handles a ConnectionStateArgs for the local client.
/// </summary>
/// <param name="connectionStateArgs"></param>
private void Multipass_OnClientConnectionState(ClientConnectionStateArgs connectionStateArgs)
{
OnClientConnectionState?.Invoke(connectionStateArgs);
}
/// <summary>
/// Handles a ConnectionStateArgs for the local server.
/// </summary>
/// <param name="connectionStateArgs"></param>
private void Multipass_OnServerConnectionState(ServerConnectionStateArgs connectionStateArgs)
{
OnServerConnectionState?.Invoke(connectionStateArgs);
TryResetClientIds(false);
}
/// <summary>
/// Handles a ConnectionStateArgs for a remote client.
/// </summary>
/// <param name="connectionStateArgs"></param>
private void Multipass_OnRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs)
{
/* When starting Multipass needs to get a new
* connectionId to be used within FN. This is the 'ClientId'
* that is passed around for ownership, rpcs, ect.
*
* The new connectionId will be linked with the connectionId
* from the transport, named transportConnectionid.
*
* When data arrives the transportStateId is used as a key
* in fromClientIds, where Multipass Id is returned. The argument values
* are then overwritten with the MultipassId.
*
* When data is being sent the same process is performed but reversed.
* The connectionId is looked up in toClientIds, where the transportConnectionId
* is output. Then as before the argument values are overwritten with the
* transportConnectionId. */
int transportIndex = connectionStateArgs.TransportIndex;
int transportId = connectionStateArgs.ConnectionId;
int multipassId;
Dictionary<int, int> transportToMultipass = _transportToMultipass[transportIndex];
//Started.
if (connectionStateArgs.ConnectionState == RemoteConnectionState.Started)
{
multipassId = _availableIds.Dequeue();
transportToMultipass[transportId] = multipassId;
_multipassToTransport[multipassId] = new TransportIdData(transportId, transportIndex);
}
//Stopped.
else
{
if (!GetMultipassId(transportIndex, transportId, out multipassId))
return;
_availableIds.Enqueue(multipassId);
transportToMultipass.Remove(transportId);
_multipassToTransport.Remove(multipassId);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
//Remove packets held for connection from latency simulator.
base.NetworkManager.TransportManager.LatencySimulator.RemovePendingForConnection(multipassId);
#endif
}
connectionStateArgs.ConnectionId = multipassId;
OnRemoteConnectionState?.Invoke(connectionStateArgs);
}
#endregion
#region Iterating.
/// <summary>
/// Processes data received by the socket.
/// </summary>
/// <param name="server">True to process data received on the server.</param>
public override void IterateIncoming(bool server)
{
foreach (Transport t in _transports)
t.IterateIncoming(server);
}
/// <summary>
/// Processes data to be sent by the socket.
/// </summary>
/// <param name="server">True to process data received on the server.</param>
public override void IterateOutgoing(bool server)
{
foreach (Transport t in _transports)
t.IterateOutgoing(server);
}
#endregion
#region ReceivedData.
/// <summary>
/// Called when client receives data.
/// </summary>
public override event Action<ClientReceivedDataArgs> OnClientReceivedData;
/// <summary>
/// Handles a ClientReceivedDataArgs.
/// </summary>
/// <param name="receivedDataArgs"></param>
private void Multipass_OnClientReceivedData(ClientReceivedDataArgs receivedDataArgs)
{
OnClientReceivedData?.Invoke(receivedDataArgs);
}
/// <summary>
/// Called when server receives data.
/// </summary>
public override event Action<ServerReceivedDataArgs> OnServerReceivedData;
/// <summary>
/// Handles a ClientReceivedDataArgs.
/// </summary>
/// <param name="receivedDataArgs"></param>
private void Multipass_OnServerReceivedData(ServerReceivedDataArgs receivedDataArgs)
{
int multipassId;
if (!GetMultipassId(receivedDataArgs.TransportIndex, receivedDataArgs.ConnectionId, out multipassId))
return;
receivedDataArgs.ConnectionId = multipassId;
OnServerReceivedData?.Invoke(receivedDataArgs);
}
#endregion
#region Sending.
/// <summary>
/// Sends to the server on ClientTransport.
/// </summary>
/// <param name="channelId">Channel to use.</param>
/// /// <param name="segment">Data to send.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void SendToServer(byte channelId, ArraySegment<byte> segment)
{
if (ClientTransport != null)
ClientTransport.SendToServer(channelId, segment);
}
/// <summary>
/// Sends data to a client.
/// </summary>
/// <param name="channelId"></param>
/// <param name="segment"></param>
/// <param name="connectionId"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
{
TransportIdData data;
if (GetTransportIdData(connectionId, out data))
_transports[data.TransportIndex].SendToClient(channelId, segment, data.TransportId);
}
#endregion
#region Configuration.
/// <summary>
/// Returns if GlobalServerActions is true and if not logs an error.
/// </summary>
/// <returns></returns>
private bool UseGlobalServerActionsWithError(string methodText)
{
if (!GlobalServerActions)
{
base.NetworkManager.LogError($"Method {methodText} is not supported while GlobalServerActions is false.");
return false;
}
else
{
return true;
}
}
/// <summary>
/// Returns if ClientTransport is set and if not logs an error.
/// </summary>
/// <param name="methodText"></param>
/// <returns></returns>
private bool IsClientTransportSetWithError(string methodText)
{
if (ClientTransport == null)
{
base.NetworkManager.LogError($"ClientTransport is not set. Use SetClientTransport before calling {methodText}.");
return false;
}
else
{
return true;
}
}
/// <summary>
/// Populates the availableIds collection.
/// </summary>
private void CreateAvailableIds()
{
_availableIds.Clear();
for (int i = 0; i < short.MaxValue; i++)
_availableIds.Enqueue(i);
}
/// <summary>
/// Sets the client transport to the first of type.
/// </summary>
/// <typeparam name="T"></typeparam>
public void SetClientTransport<T>()
{
int index = -1;
for (int i = 0; i < _transports.Count; i++)
{
if (_transports[i].GetType() == typeof(T))
{
index = i;
break;
}
}
SetClientTransport(index);
}
/// <summary>
/// Sets the client transport to the first of type T.
/// </summary>
/// <typeparam name="T"></typeparam>
public void SetClientTransport(Type type)
{
int index = -1;
for (int i = 0; i < _transports.Count; i++)
{
if (_transports[i].GetType() == type)
{
index = i;
break;
}
}
SetClientTransport(index);
}
/// <summary>
/// Sets the client transport to the matching reference of transport.
/// </summary>
/// <param name="transport"></param>
public void SetClientTransport(Transport transport)
{
int index = -1;
for (int i = 0; i < _transports.Count; i++)
{
if (_transports[i] == transport)
{
index = i;
break;
}
}
SetClientTransport(index);
}
/// <summary>
/// Sets the client transport to the transport on index.
/// </summary>
/// <param name="index"></param>
public void SetClientTransport(int index)
{
if (!IndexInRange(index, true))
return;
ClientTransport = _transports[index];
}
/// <summary>
/// Gets the Transport on index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public Transport GetTransport(int index)
{
if (!IndexInRange(index, true))
return null;
return _transports[index];
}
/// <summary>
/// Gets the Transport on of type T.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T GetTransport<T>()
{
foreach (Transport t in _transports)
{
if (t.GetType() == typeof(T))
return (T)(object)t;
}
return default(T);
}
/// <summary>
/// Returns if the transport for connectionId is a local transport.
/// While true several security checks are disabled.
/// </summary>
public override bool IsLocalTransport(int connectionid)
{
//If able to get transport data return value from transport.
if (GetTransportIdData(connectionid, out TransportIdData data))
return _transports[data.TransportIndex].IsLocalTransport(data.TransportId);
//Otherwise return false forcing checks.
else
return false;
}
/// <summary>
/// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned.
/// The first transport is used.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetMaximumClients()
{
base.NetworkManager.LogError($"This method is not supported. Use GetMaximumClients(transportIndex) instead.");
return -1;
}
/// <summary>
/// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned.
/// The first transport is used.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetMaximumClients(int transportIndex)
{
if (!IndexInRange(transportIndex, true))
return -1;
return _transports[transportIndex].GetMaximumClients();
}
/// <summary>
/// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect.
/// This sets the value to the transport on the first index.
/// </summary>
/// <param name="value"></param>
public override void SetMaximumClients(int value)
{
base.NetworkManager.LogError($"This method is not supported. Use SetMaximumClients(value, transportIndex) instead.");
}
/// <summary>
/// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect.
/// This sets the value to the transport on index.
/// </summary>
/// <param name="value"></param>
public void SetMaximumClients(int value, int transportIndex)
{
if (!IndexInRange(transportIndex, true))
return;
_transports[transportIndex].SetMaximumClients(value);
}
/// <summary>
/// Sets which address the client will connect to.
/// This will set the address for every transport.
/// </summary>
/// <param name="address"></param>
public override void SetClientAddress(string address)
{
foreach (Transport t in _transports)
t.SetClientAddress(address);
}
public override void SetServerBindAddress(string address, IPAddressType addressType)
{
base.NetworkManager.LogError($"This method is not supported. Use SetServerBindAddress(address, transportIndex) instead.");
}
/// Sets which address the server will bind to.
/// This is called on the transport of index.
/// </summary>
/// <param name="address"></param>
public void SetServerBindAddress(string address, IPAddressType addressType, int index)
{
if (!IndexInRange(index, true))
return;
_transports[index].SetServerBindAddress(address, addressType);
}
/// <summary>
/// Sets which port to use on the first transport.
/// </summary>
public override void SetPort(ushort port)
{
base.NetworkManager.LogError($"This method is not supported. Use SetPort(port, transportIndex) instead.");
}
/// <summary>
/// Sets which port to use on transport of index.
/// </summary>
public void SetPort(ushort port, int index)
{
if (!IndexInRange(index, true))
return;
_transports[index].SetPort(port);
}
#endregion
#region Start and stop.
/// <summary>
/// Starts the local server or client using configured settings on the first transport.
/// </summary>
/// <param name="server">True to start server.</param>
public override bool StartConnection(bool server)
{
//Server.
if (server)
{
if (!UseGlobalServerActionsWithError("StartConnection"))
return false;
bool success = true;
for (int i = 0; i < _transports.Count; i++)
{
if (!StartConnection(true, i))
success = false;
}
return success;
}
//Client.
else
{
if (IsClientTransportSetWithError("StartConnection"))
return StartConnection(false, ClientTransport.Index);
else
return false;
}
}
/// <summary>
/// Starts the local server or client using configured settings on transport of index.
/// </summary>
/// <param name="server">True to start server.</param>
public bool StartConnection(bool server, int index)
{
if (server)
{
return StartServer(index);
}
else
{
if (IsClientTransportSetWithError("StartConnection"))
return StartClient();
else
return false;
}
}
/// <summary>
/// Stops the local server or client on the first transport.
/// </summary>
/// <param name="server">True to stop server.</param>
public override bool StopConnection(bool server)
{
//Server
if (server)
{
if (!UseGlobalServerActionsWithError("StopConnection"))
return false;
bool success = true;
for (int i = 0; i < _transports.Count; i++)
{
if (!StopConnection(true, i))
success = false;
}
return success;
}
//Client.
else
{
if (IsClientTransportSetWithError("StopConnection"))
return StopConnection(false, ClientTransport.Index);
else
return false;
}
}
/// <summary>
/// Stops the local server or client on transport of index.
/// </summary>
/// <param name="server">True to stop server.</param>
public bool StopConnection(bool server, int index)
{
if (server)
{
return StopServer(index);
}
else
{
if (IsClientTransportSetWithError("StopConnection"))
return StopClient();
else
return false;
}
}
/// <summary>
/// Stops a remote client from the server, disconnecting the client.
/// </summary>
/// <param name="connectionId">ConnectionId of the client to disconnect.</param>
/// <param name="immediately">True to abrutly stp the client socket without waiting socket thread.</param>
public override bool StopConnection(int connectionId, bool immediately)
{
return StopClient(connectionId, immediately);
}
/// <summary>
/// Stops the server connection on transportIndex.
/// </summary>
/// <param name="sendDisconnectMessage">True to send a disconnect message to connections before stopping them.</param>
/// <param name="transportIndex">Index of transport to stop on.</param>
public bool StopServerConnection(bool sendDisconnectMessage, int transportIndex)
{
if (sendDisconnectMessage)
{
//Get connectionIds as ServerManager knows them.
int[] multipassIds = _transportToMultipass[transportIndex].Keys.ToArray();
//Tell serve manager to write disconnect for those ids.
base.NetworkManager.ServerManager.SendDisconnectMessages(multipassIds);
//Iterate outgoing on transport which is being stopped.
_transports[transportIndex].IterateOutgoing(true);
}
return StopConnection(true, transportIndex);
}
/// <summary>
/// Stops both client and server on all transports.
/// </summary>
public override void Shutdown()
{
foreach (Transport t in _transports)
{
//Stops client then server connections.
t.StopConnection(false);
t.StopConnection(true);
}
}
#region Privates.
/// <summary>
/// Starts server of transport on index.
/// </summary>
/// <returns>True if there were no blocks. A true response does not promise a socket will or has connected.</returns>
private bool StartServer(int index)
{
if (!IndexInRange(index, true))
return false;
return _transports[index].StartConnection(true);
}
/// <summary>
/// Stops server of transport on index.
/// </summary>
private bool StopServer(int index)
{
if (!IndexInRange(index, true))
return false;
return _transports[index].StopConnection(true);
}
/// <summary>
/// Starts the client on ClientTransport.
/// </summary>
/// <param name="address"></param>
/// <returns>True if there were no blocks. A true response does not promise a socket will or has connected.</returns>
private bool StartClient()
{
return ClientTransport.StartConnection(false);
}
/// <summary>
/// Stops the client on ClientTransport.
/// </summary>
private bool StopClient()
{
return ClientTransport.StopConnection(false);
}
/// <summary>
/// Stops a remote client on the server.
/// </summary>
/// <param name="connectionId"></param>
/// <param name="immediately">True to abrutly stp the client socket without waiting socket thread.</param>
private bool StopClient(int connectionId, bool immediately)
{
TransportIdData data;
if (!GetTransportIdData(connectionId, out data))
return false;
return _transports[data.TransportIndex].StopConnection(data.TransportId, immediately);
}
#endregion
#endregion
#region Channels.
/// <summary>
/// Gets the MTU for a channel on the first transport. This should take header size into consideration.
/// For example, if MTU is 1200 and a packet header for this channel is 10 in size, this method should return 1190.
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
public override int GetMTU(byte channel)
{
return GetMTU(channel, 0);
}
/// <summary>
/// Gets the MTU for a channel of transport on index. This should take header size into consideration.
/// For example, if MTU is 1200 and a packet header for this channel is 10 in size, this method should return 1190.
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
public int GetMTU(byte channel, int index)
{
if (!IndexInRange(index, true))
return -1;
return _transports[index].GetMTU(channel);
}
#endregion
#region Misc.
/// <summary>
/// Returns if an index is within range of the Transports collection.
/// </summary>
private bool IndexInRange(int index, bool error)
{
if (index >= _transports.Count || index < 0)
{
if (error)
base.NetworkManager.LogError($"Index of {index} is out of Transports range.");
return false;
}
else
{
return true;
}
}
//perf change events to direct calls in transports.
public override void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs) { }
public override void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs) { }
public override void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs) { }
public override void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs) { }
public override void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs) { }
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 314b449d3505bd24487ba69b61c2fda5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e4f9d944e2ca8484587859cf4ec80b6c
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 43760836a07366846a82fe7f158bd84e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4dad76b44081bb54b97e6da2e0e6f26d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,307 @@
using FishNet.Managing.Logging;
using LiteNetLib;
using LiteNetLib.Layers;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine;
namespace FishNet.Transporting.Tugboat.Client
{
public class ClientSocket : CommonSocket
{
~ClientSocket()
{
StopConnection();
}
#region Private.
#region Configuration.
/// <summary>
/// Address to bind server to.
/// </summary>
private string _address = string.Empty;
/// <summary>
/// Port used by server.
/// </summary>
private ushort _port;
/// <summary>
/// MTU sizes for each channel.
/// </summary>
private int _mtu;
#endregion
#region Queues.
/// <summary>
/// Changes to the sockets local connection state.
/// </summary>
private Queue<LocalConnectionState> _localConnectionStates = new Queue<LocalConnectionState>();
/// <summary>
/// Inbound messages which need to be handled.
/// </summary>
private Queue<Packet> _incoming = new Queue<Packet>();
/// <summary>
/// Outbound messages which need to be handled.
/// </summary>
private Queue<Packet> _outgoing = new Queue<Packet>();
#endregion
/// <summary>
/// Client socket manager.
/// </summary>
private NetManager _client;
/// <summary>
/// How long in seconds until client times from server.
/// </summary>
private int _timeout;
/// <summary>
/// PacketLayer to use with LiteNetLib.
/// </summary>
private PacketLayerBase _packetLayer;
/// <summary>
/// Locks the NetManager to stop it.
/// </summary>
private readonly object _stopLock = new object();
#endregion
/// <summary>
/// Initializes this for use.
/// </summary>
/// <param name="t"></param>
internal void Initialize(Transport t, int unreliableMTU, PacketLayerBase packetLayer)
{
base.Transport = t;
_mtu = unreliableMTU;
_packetLayer = packetLayer;
}
/// <summary>
/// Updates the Timeout value as seconds.
/// </summary>
internal void UpdateTimeout(int timeout)
{
_timeout = timeout;
base.UpdateTimeout(_client, timeout);
}
/// <summary>
/// Threaded operation to process client actions.
/// </summary>
private void ThreadedSocket()
{
EventBasedNetListener listener = new EventBasedNetListener();
listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent;
listener.PeerConnectedEvent += Listener_PeerConnectedEvent;
listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent;
_client = new NetManager(listener, _packetLayer);
_client.MtuOverride = (_mtu + NetConstants.FragmentedHeaderTotalSize);
UpdateTimeout(_timeout);
_localConnectionStates.Enqueue(LocalConnectionState.Starting);
_client.Start();
_client.Connect(_address, _port, string.Empty);
}
/// <summary>
/// Stops the socket on a new thread.
/// </summary>
private void StopSocketOnThread()
{
if (_client == null)
return;
Task t = Task.Run(() =>
{
lock (_stopLock)
{
_client?.Stop();
_client = null;
}
//If not stopped yet also enqueue stop.
if (base.GetConnectionState() != LocalConnectionState.Stopped)
_localConnectionStates.Enqueue(LocalConnectionState.Stopped);
});
}
/// <summary>
/// Starts the client connection.
/// </summary>
/// <param name="address"></param>
/// <param name="port"></param>
/// <param name="channelsCount"></param>
/// <param name="pollTime"></param>
internal bool StartConnection(string address, ushort port)
{
if (base.GetConnectionState() != LocalConnectionState.Stopped)
return false;
base.SetConnectionState(LocalConnectionState.Starting, false);
//Assign properties.
_port = port;
_address = address;
ResetQueues();
Task t = Task.Run(() => ThreadedSocket());
return true;
}
/// <summary>
/// Stops the local socket.
/// </summary>
internal bool StopConnection(DisconnectInfo? info = null)
{
if (base.GetConnectionState() == LocalConnectionState.Stopped || base.GetConnectionState() == LocalConnectionState.Stopping)
return false;
if (info != null)
base.Transport.NetworkManager.Log($"Local client disconnect reason: {info.Value.Reason}.");
base.SetConnectionState(LocalConnectionState.Stopping, false);
StopSocketOnThread();
return true;
}
/// <summary>
/// Resets queues.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetQueues()
{
_localConnectionStates.Clear();
base.ClearPacketQueue(ref _incoming);
base.ClearPacketQueue(ref _outgoing);
}
/// <summary>
/// Called when disconnected from the server.
/// </summary>
private void Listener_PeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo)
{
StopConnection(disconnectInfo);
}
/// <summary>
/// Called when connected to the server.
/// </summary>
private void Listener_PeerConnectedEvent(NetPeer peer)
{
_localConnectionStates.Enqueue(LocalConnectionState.Started);
}
/// <summary>
/// Called when data is received from a peer.
/// </summary>
private void Listener_NetworkReceiveEvent(NetPeer fromPeer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
{
base.Listener_NetworkReceiveEvent(_incoming, fromPeer, reader, deliveryMethod, _mtu);
}
/// <summary>
/// Dequeues and processes outgoing.
/// </summary>
private void DequeueOutgoing()
{
NetPeer peer = null;
if (_client != null)
peer = _client.FirstPeer;
//Server connection hasn't been made.
if (peer == null)
{
/* Only dequeue outgoing because other queues might have
* relevant information, such as the local connection queue. */
base.ClearPacketQueue(ref _outgoing);
}
else
{
int count = _outgoing.Count;
for (int i = 0; i < count; i++)
{
Packet outgoing = _outgoing.Dequeue();
ArraySegment<byte> segment = outgoing.GetArraySegment();
DeliveryMethod dm = (outgoing.Channel == (byte)Channel.Reliable) ?
DeliveryMethod.ReliableOrdered : DeliveryMethod.Unreliable;
//If over the MTU.
if (outgoing.Channel == (byte)Channel.Unreliable && segment.Count > _mtu)
{
base.Transport.NetworkManager.LogWarning($"Client is sending of {segment.Count} length on the unreliable channel, while the MTU is only {_mtu}. The channel has been changed to reliable for this send.");
dm = DeliveryMethod.ReliableOrdered;
}
peer.Send(segment.Array, segment.Offset, segment.Count, dm);
outgoing.Dispose();
}
}
}
/// <summary>
/// Allows for Outgoing queue to be iterated.
/// </summary>
internal void IterateOutgoing()
{
DequeueOutgoing();
}
/// <summary>
/// Iterates the Incoming queue.
/// </summary>
internal void IterateIncoming()
{
_client?.PollEvents();
/* Run local connection states first so we can begin
* to read for data at the start of the frame, as that's
* where incoming is read. */
while (_localConnectionStates.Count > 0)
base.SetConnectionState(_localConnectionStates.Dequeue(), false);
//Not yet started, cannot continue.
LocalConnectionState localState = base.GetConnectionState();
if (localState != LocalConnectionState.Started)
{
ResetQueues();
//If stopped try to kill task.
if (localState == LocalConnectionState.Stopped)
{
StopSocketOnThread();
return;
}
}
/* Incoming. */
while (_incoming.Count > 0)
{
Packet incoming = _incoming.Dequeue();
ClientReceivedDataArgs dataArgs = new ClientReceivedDataArgs(
incoming.GetArraySegment(),
(Channel)incoming.Channel, base.Transport.Index);
base.Transport.HandleClientReceivedDataArgs(dataArgs);
//Dispose of packet.
incoming.Dispose();
}
}
/// <summary>
/// Sends a packet to the server.
/// </summary>
internal void SendToServer(byte channelId, ArraySegment<byte> segment)
{
//Not started, cannot send.
if (base.GetConnectionState() != LocalConnectionState.Started)
return;
base.Send(ref _outgoing, channelId, segment, -1, _mtu);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7944de5e4da77594db036e276174ee60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,125 @@
using FishNet.Utility.Performance;
using LiteNetLib;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace FishNet.Transporting.Tugboat
{
public abstract class CommonSocket
{
#region Public.
/// <summary>
/// Current ConnectionState.
/// </summary>
private LocalConnectionState _connectionState = LocalConnectionState.Stopped;
/// <summary>
/// Returns the current ConnectionState.
/// </summary>
/// <returns></returns>
internal LocalConnectionState GetConnectionState()
{
return _connectionState;
}
/// <summary>
/// Sets a new connection state.
/// </summary>
/// <param name="connectionState"></param>
protected void SetConnectionState(LocalConnectionState connectionState, bool asServer)
{
//If state hasn't changed.
if (connectionState == _connectionState)
return;
_connectionState = connectionState;
if (asServer)
Transport.HandleServerConnectionState(new ServerConnectionStateArgs(connectionState, Transport.Index));
else
Transport.HandleClientConnectionState(new ClientConnectionStateArgs(connectionState, Transport.Index));
}
#endregion
#region Protected.
/// <summary>
/// Transport controlling this socket.
/// </summary>
protected Transport Transport = null;
#endregion
/// <summary>
/// Sends data to connectionId.
/// </summary>
internal void Send(ref Queue<Packet> queue, byte channelId, ArraySegment<byte> segment, int connectionId, int mtu)
{
if (GetConnectionState() != LocalConnectionState.Started)
return;
//ConnectionId isn't used from client to server.
Packet outgoing = new Packet(connectionId, segment, channelId, mtu);
queue.Enqueue(outgoing);
}
/// <summary>
/// Updates the timeout for NetManager.
/// </summary>
protected void UpdateTimeout(NetManager netManager, int timeout)
{
if (netManager == null)
return;
timeout = (timeout == 0) ? int.MaxValue : Math.Min(int.MaxValue, (timeout * 1000));
netManager.DisconnectTimeout = timeout;
}
/// <summary>
/// Clears a queue using Packet type.
/// </summary>
/// <param name="queue"></param>
internal void ClearPacketQueue(ref ConcurrentQueue<Packet> queue)
{
while (queue.TryDequeue(out Packet p))
p.Dispose();
}
/// <summary>
/// Clears a queue using Packet type.
/// </summary>
/// <param name="queue"></param>
internal void ClearPacketQueue(ref Queue<Packet> queue)
{
int count = queue.Count;
for (int i = 0; i < count; i++)
{
Packet p = queue.Dequeue();
p.Dispose();
}
}
/// <summary>
/// Called when data is received.
/// </summary>
internal virtual void Listener_NetworkReceiveEvent(Queue<Packet> queue, NetPeer fromPeer, NetPacketReader reader, DeliveryMethod deliveryMethod, int mtu)
{
//Set buffer.
int dataLen = reader.AvailableBytes;
//Prefer to max out returned array to mtu to reduce chance of resizing.
int arraySize = Math.Max(dataLen, mtu);
byte[] data = ByteArrayPool.Retrieve(arraySize);
reader.GetBytes(data, dataLen);
//Id.
int id = fromPeer.Id;
//Channel.
byte channel = (deliveryMethod == DeliveryMethod.Unreliable) ?
(byte)Channel.Unreliable : (byte)Channel.Reliable;
//Add to packets.
Packet packet = new Packet(id, data, dataLen, channel);
queue.Enqueue(packet);
//Recycle reader.
reader.Recycle();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 329169cdf51866c43a8c42e8aeb291fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,523 @@
using FishNet.Connection;
using FishNet.Managing.Logging;
using LiteNetLib;
using LiteNetLib.Layers;
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine;
namespace FishNet.Transporting.Tugboat.Server
{
public class ServerSocket : CommonSocket
{
#region Public.
/// <summary>
/// Gets the current ConnectionState of a remote client on the server.
/// </summary>
/// <param name="connectionId">ConnectionId to get ConnectionState for.</param>
internal RemoteConnectionState GetConnectionState(int connectionId)
{
NetPeer peer = GetNetPeer(connectionId, false);
if (peer == null || peer.ConnectionState != ConnectionState.Connected)
return RemoteConnectionState.Stopped;
else
return RemoteConnectionState.Started;
}
#endregion
#region Private.
#region Configuration.
/// <summary>
/// Port used by server.
/// </summary>
private ushort _port;
/// <summary>
/// Maximum number of allowed clients.
/// </summary>
private int _maximumClients;
/// <summary>
/// MTU size per packet.
/// </summary>
private int _mtu;
#endregion
#region Queues.
/// <summary>
/// Changes to the sockets local connection state.
/// </summary>
private Queue<LocalConnectionState> _localConnectionStates = new Queue<LocalConnectionState>();
/// <summary>
/// Inbound messages which need to be handled.
/// </summary>
private Queue<Packet> _incoming = new Queue<Packet>();
/// <summary>
/// Outbound messages which need to be handled.
/// </summary>
private Queue<Packet> _outgoing = new Queue<Packet>();
/// <summary>
/// ConnectionEvents which need to be handled.
/// </summary>
private Queue<RemoteConnectionEvent> _remoteConnectionEvents = new Queue<RemoteConnectionEvent>();
#endregion
/// <summary>
/// Key required to connect.
/// </summary>
private string _key = string.Empty;
/// <summary>
/// How long in seconds until client times from server.
/// </summary>
private int _timeout;
/// <summary>
/// Server socket manager.
/// </summary>
private NetManager _server;
/// <summary>
/// IPv4 address to bind server to.
/// </summary>
private string _ipv4BindAddress;
/// <summary>
/// IPv6 address to bind server to.
/// </summary>
private string _ipv6BindAddress;
/// <summary>
/// PacketLayer to use with LiteNetLib.
/// </summary>
private PacketLayerBase _packetLayer;
/// <summary>
/// Locks the NetManager to stop it.
/// </summary>
private readonly object _stopLock = new object();
#endregion
~ServerSocket()
{
StopConnection();
}
/// <summary>
/// Initializes this for use.
/// </summary>
/// <param name="t"></param>
internal void Initialize(Transport t, int unreliableMTU, PacketLayerBase packetLayer)
{
base.Transport = t;
_mtu = unreliableMTU;
_packetLayer = packetLayer;
}
/// <summary>
/// Updates the Timeout value as seconds.
/// </summary>
internal void UpdateTimeout(int timeout)
{
_timeout = timeout;
base.UpdateTimeout(_server, timeout);
}
/// <summary>
/// Threaded operation to process server actions.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThreadedSocket()
{
EventBasedNetListener listener = new EventBasedNetListener();
listener.ConnectionRequestEvent += Listener_ConnectionRequestEvent;
listener.PeerConnectedEvent += Listener_PeerConnectedEvent;
listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent;
listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent;
_server = new NetManager(listener, _packetLayer);
_server.MtuOverride = (_mtu + NetConstants.FragmentedHeaderTotalSize);
UpdateTimeout(_timeout);
//Set bind addresses.
IPAddress ipv4;
IPAddress ipv6;
//Set ipv4
if (!string.IsNullOrEmpty(_ipv4BindAddress))
{
if (!IPAddress.TryParse(_ipv4BindAddress, out ipv4))
ipv4 = null;
//If unable to parse try to get address another way.
if (ipv4 == null)
{
IPHostEntry hostEntry = Dns.GetHostEntry(_ipv4BindAddress);
if (hostEntry.AddressList.Length > 0)
{
ipv4 = hostEntry.AddressList[0];
base.Transport.NetworkManager.Log($"IPv4 could not parse correctly but was resolved to {ipv4.ToString()}");
}
}
}
else
{
IPAddress.TryParse("0.0.0.0", out ipv4);
}
//Set ipv6.
if (!string.IsNullOrEmpty(_ipv6BindAddress))
{
if (!IPAddress.TryParse(_ipv6BindAddress, out ipv6))
ipv6 = null;
}
else
{
IPAddress.TryParse("0:0:0:0:0:0:0:0", out ipv6);
}
string ipv4FailText = (ipv4 == null) ? $"IPv4 address {_ipv4BindAddress} failed to parse. " : string.Empty;
string ipv6FailText = (ipv6 == null) ? $"IPv6 address {_ipv6BindAddress} failed to parse. " : string.Empty;
if (ipv4FailText != string.Empty || ipv6FailText != string.Empty)
{
base.Transport.NetworkManager.Log($"{ipv4FailText}{ipv6FailText}Clear the bind address field to use any bind address.");
StopConnection();
return;
}
bool startResult = _server.Start(ipv4, ipv6, _port);
//If started succcessfully.
if (startResult)
{
_localConnectionStates.Enqueue(LocalConnectionState.Started);
}
//Failed to start.
else
{
base.Transport.NetworkManager.LogError($"Server failed to start. This usually occurs when the specified port is unavailable, be it closed or already in use.");
StopConnection();
}
}
/// <summary>
/// Stops the socket on a new thread.
/// </summary>
private void StopSocketOnThread()
{
if (_server == null)
return;
Task t = Task.Run(() =>
{
lock (_stopLock)
{
_server?.Stop();
_server = null;
}
//If not stopped yet also enqueue stop.
if (base.GetConnectionState() != LocalConnectionState.Stopped)
_localConnectionStates.Enqueue(LocalConnectionState.Stopped);
});
}
/// <summary>
/// Gets the address of a remote connection Id.
/// </summary>
/// <param name="connectionId"></param>
/// <returns>Returns string.empty if Id is not found.</returns>
internal string GetConnectionAddress(int connectionId)
{
NetPeer peer = GetNetPeer(connectionId, false);
return peer.EndPoint.Address.ToString();
}
/// <summary>
/// Returns a NetPeer for connectionId.
/// </summary>
/// <param name="connectionId"></param>
/// <returns></returns>
private NetPeer GetNetPeer(int connectionId, bool connectedOnly)
{
if (_server != null)
{
NetPeer peer = _server.GetPeerById(connectionId);
if (connectedOnly && peer != null && peer.ConnectionState != ConnectionState.Connected)
peer = null;
return peer;
}
else
{
return null;
}
}
/// <summary>
/// Starts the server.
/// </summary>
internal bool StartConnection(ushort port, int maximumClients, string ipv4BindAddress, string ipv6BindAddress)
{
if (base.GetConnectionState() != LocalConnectionState.Stopped)
return false;
base.SetConnectionState(LocalConnectionState.Starting, true);
//Assign properties.
_port = port;
_maximumClients = maximumClients;
_ipv4BindAddress = ipv4BindAddress;
_ipv6BindAddress = ipv6BindAddress;
ResetQueues();
Task t = Task.Run(() => ThreadedSocket());
return true;
}
/// <summary>
/// Stops the local socket.
/// </summary>
internal bool StopConnection()
{
if (_server == null || base.GetConnectionState() == LocalConnectionState.Stopped || base.GetConnectionState() == LocalConnectionState.Stopping)
return false;
_localConnectionStates.Enqueue(LocalConnectionState.Stopping);
StopSocketOnThread();
return true;
}
/// <summary>
/// Stops a remote client disconnecting the client from the server.
/// </summary>
/// <param name="connectionId">ConnectionId of the client to disconnect.</param>
internal bool StopConnection(int connectionId)
{
//Server isn't running.
if (_server == null || base.GetConnectionState() != LocalConnectionState.Started)
return false;
NetPeer peer = GetNetPeer(connectionId, false);
if (peer == null)
return false;
try
{
peer.Disconnect();
//Let LiteNetLib get the disconnect event which will enqueue a remote connection state.
//base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Stopped, connectionId, base.Transport.Index));
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Resets queues.
/// </summary>
private void ResetQueues()
{
_localConnectionStates.Clear();
base.ClearPacketQueue(ref _incoming);
base.ClearPacketQueue(ref _outgoing);
_remoteConnectionEvents.Clear();
}
/// <summary>
/// Called when a peer disconnects or times out.
/// </summary>
private void Listener_PeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo)
{
_remoteConnectionEvents.Enqueue(new RemoteConnectionEvent(false, peer.Id));
}
/// <summary>
/// Called when a peer completes connection.
/// </summary>
private void Listener_PeerConnectedEvent(NetPeer peer)
{
_remoteConnectionEvents.Enqueue(new RemoteConnectionEvent(true, peer.Id));
}
/// <summary>
/// Called when data is received from a peer.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Listener_NetworkReceiveEvent(NetPeer fromPeer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
{
//If over the MTU.
if (reader.AvailableBytes > _mtu)
{
_remoteConnectionEvents.Enqueue(new RemoteConnectionEvent(false, fromPeer.Id));
fromPeer.Disconnect();
}
else
{
base.Listener_NetworkReceiveEvent(_incoming, fromPeer, reader, deliveryMethod, _mtu);
}
}
/// <summary>
/// Called when a remote connection request is made.
/// </summary>
private void Listener_ConnectionRequestEvent(ConnectionRequest request)
{
if (_server == null)
return;
//At maximum peers.
if (_server.ConnectedPeersCount >= _maximumClients)
{
request.Reject();
return;
}
request.AcceptIfKey(_key);
}
/// <summary>
/// Dequeues and processes outgoing.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DequeueOutgoing()
{
if (base.GetConnectionState() != LocalConnectionState.Started || _server == null)
{
//Not started, clear outgoing.
base.ClearPacketQueue(ref _outgoing);
}
else
{
int count = _outgoing.Count;
for (int i = 0; i < count; i++)
{
Packet outgoing = _outgoing.Dequeue();
int connectionId = outgoing.ConnectionId;
ArraySegment<byte> segment = outgoing.GetArraySegment();
DeliveryMethod dm = (outgoing.Channel == (byte)Channel.Reliable) ?
DeliveryMethod.ReliableOrdered : DeliveryMethod.Unreliable;
//If over the MTU.
if (outgoing.Channel == (byte)Channel.Unreliable && segment.Count > _mtu)
{
base.Transport.NetworkManager.LogWarning($"Server is sending of {segment.Count} length on the unreliable channel, while the MTU is only {_mtu}. The channel has been changed to reliable for this send.");
dm = DeliveryMethod.ReliableOrdered;
}
//Send to all clients.
if (connectionId == NetworkConnection.UNSET_CLIENTID_VALUE)
{
_server.SendToAll(segment.Array, segment.Offset, segment.Count, dm);
}
//Send to one client.
else
{
NetPeer peer = GetNetPeer(connectionId, true);
//If peer is found.
if (peer != null)
peer.Send(segment.Array, segment.Offset, segment.Count, dm);
}
outgoing.Dispose();
}
}
}
/// <summary>
/// Allows for Outgoing queue to be iterated.
/// </summary>
internal void IterateOutgoing()
{
DequeueOutgoing();
}
/// <summary>
/// Iterates the Incoming queue.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void IterateIncoming()
{
_server?.PollEvents();
/* Run local connection states first so we can begin
* to read for data at the start of the frame, as that's
* where incoming is read. */
while (_localConnectionStates.Count > 0)
base.SetConnectionState(_localConnectionStates.Dequeue(), true);
//Not yet started.
LocalConnectionState localState = base.GetConnectionState();
if (localState != LocalConnectionState.Started)
{
ResetQueues();
//If stopped try to kill task.
if (localState == LocalConnectionState.Stopped)
{
StopSocketOnThread();
return;
}
}
//Handle connection and disconnection events.
while (_remoteConnectionEvents.Count > 0)
{
RemoteConnectionEvent connectionEvent = _remoteConnectionEvents.Dequeue();
RemoteConnectionState state = (connectionEvent.Connected) ? RemoteConnectionState.Started : RemoteConnectionState.Stopped;
base.Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(state, connectionEvent.ConnectionId, base.Transport.Index));
}
//Handle packets.
while (_incoming.Count > 0)
{
Packet incoming = _incoming.Dequeue();
//Make sure peer is still connected.
NetPeer peer = GetNetPeer(incoming.ConnectionId, true);
if (peer != null)
{
ServerReceivedDataArgs dataArgs = new ServerReceivedDataArgs(
incoming.GetArraySegment(),
(Channel)incoming.Channel,
incoming.ConnectionId,
base.Transport.Index);
base.Transport.HandleServerReceivedDataArgs(dataArgs);
}
incoming.Dispose();
}
}
/// <summary>
/// Sends a packet to a single, or all clients.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
{
Send(ref _outgoing, channelId, segment, connectionId, _mtu);
}
/// <summary>
/// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned.
/// </summary>
/// <returns></returns>
internal int GetMaximumClients()
{
return _maximumClients;
}
/// <summary>
/// Sets the MaximumClients value.
/// </summary>
/// <param name="value"></param>
internal void SetMaximumClients(int value)
{
_maximumClients = value;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c6703e8024041e45ae92566123865ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using FishNet.Utility.Performance;
using System;
namespace FishNet.Transporting.Tugboat
{
internal struct Packet
{
public readonly int ConnectionId;
public readonly byte[] Data;
public readonly int Length;
public readonly byte Channel;
public Packet(int connectionId, byte[] data, int length, byte channel)
{
ConnectionId = connectionId;
Data = data;
Length = length;
Channel = channel;
}
public Packet(int sender, ArraySegment<byte> segment, byte channel, int mtu)
{
//Prefer to max out returned array to mtu to reduce chance of resizing.
int arraySize = Math.Max(segment.Count, mtu);
Data = ByteArrayPool.Retrieve(arraySize);
Buffer.BlockCopy(segment.Array, segment.Offset, Data, 0, segment.Count);
ConnectionId = sender;
Length = segment.Count;
Channel = channel;
}
public ArraySegment<byte> GetArraySegment()
{
return new ArraySegment<byte>(Data, 0, Length);
}
public void Dispose()
{
ByteArrayPool.Store(Data);
}
}
}
namespace FishNet.Transporting.Tugboat.Server
{
internal struct RemoteConnectionEvent
{
public readonly bool Connected;
public readonly int ConnectionId;
public RemoteConnectionEvent(bool connected, int connectionId)
{
Connected = connected;
ConnectionId = connectionId;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80c810a1a6a8f3345bb48abfb75c804a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9af335f3230cff649a5cc0c50e34a206
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
using System.Collections.Concurrent;
using System.Threading;
namespace LiteNetLib
{
internal abstract class BaseChannel
{
protected readonly NetPeer Peer;
protected readonly ConcurrentQueue<NetPacket> OutgoingQueue;
private int _isAddedToPeerChannelSendQueue;
public int PacketsInQueue => OutgoingQueue.Count;
protected BaseChannel(NetPeer peer)
{
Peer = peer;
OutgoingQueue = new ConcurrentQueue<NetPacket>();
}
public void AddToQueue(NetPacket packet)
{
OutgoingQueue.Enqueue(packet);
AddToPeerChannelSendQueue();
}
protected void AddToPeerChannelSendQueue()
{
if (Interlocked.CompareExchange(ref _isAddedToPeerChannelSendQueue, 1, 0) == 0)
{
Peer.AddToReliableChannelSendQueue(this);
}
}
public bool SendAndCheckQueue()
{
bool hasPacketsToSend = SendNextPackets();
if (!hasPacketsToSend)
Interlocked.Exchange(ref _isAddedToPeerChannelSendQueue, 0);
return hasPacketsToSend;
}
protected abstract bool SendNextPackets();
public abstract bool ProcessPacket(NetPacket packet);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70b7c357a9f57f5479c5d94550d26280
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,134 @@
using System.Net;
using System.Threading;
using LiteNetLib.Utils;
namespace LiteNetLib
{
internal enum ConnectionRequestResult
{
None,
Accept,
Reject,
RejectForce
}
public class ConnectionRequest
{
private readonly NetManager _listener;
private int _used;
public NetDataReader Data => InternalPacket.Data;
internal ConnectionRequestResult Result { get; private set; }
internal NetConnectRequestPacket InternalPacket;
public readonly IPEndPoint RemoteEndPoint;
internal void UpdateRequest(NetConnectRequestPacket connectRequest)
{
//old request
if (connectRequest.ConnectionTime < InternalPacket.ConnectionTime)
return;
if (connectRequest.ConnectionTime == InternalPacket.ConnectionTime &&
connectRequest.ConnectionNumber == InternalPacket.ConnectionNumber)
return;
InternalPacket = connectRequest;
}
private bool TryActivate()
{
return Interlocked.CompareExchange(ref _used, 1, 0) == 0;
}
internal ConnectionRequest(IPEndPoint remoteEndPoint, NetConnectRequestPacket requestPacket, NetManager listener)
{
InternalPacket = requestPacket;
RemoteEndPoint = remoteEndPoint;
_listener = listener;
}
public NetPeer AcceptIfKey(string key)
{
if (!TryActivate())
return null;
try
{
if (Data.GetString() == key)
Result = ConnectionRequestResult.Accept;
}
catch
{
NetDebug.WriteError("[AC] Invalid incoming data");
}
if (Result == ConnectionRequestResult.Accept)
return _listener.OnConnectionSolved(this, null, 0, 0);
Result = ConnectionRequestResult.Reject;
_listener.OnConnectionSolved(this, null, 0, 0);
return null;
}
/// <summary>
/// Accept connection and get new NetPeer as result
/// </summary>
/// <returns>Connected NetPeer</returns>
public NetPeer Accept()
{
if (!TryActivate())
return null;
Result = ConnectionRequestResult.Accept;
return _listener.OnConnectionSolved(this, null, 0, 0);
}
public void Reject(byte[] rejectData, int start, int length, bool force)
{
if (!TryActivate())
return;
Result = force ? ConnectionRequestResult.RejectForce : ConnectionRequestResult.Reject;
_listener.OnConnectionSolved(this, rejectData, start, length);
}
public void Reject(byte[] rejectData, int start, int length)
{
Reject(rejectData, start, length, false);
}
public void RejectForce(byte[] rejectData, int start, int length)
{
Reject(rejectData, start, length, true);
}
public void RejectForce()
{
Reject(null, 0, 0, true);
}
public void RejectForce(byte[] rejectData)
{
Reject(rejectData, 0, rejectData.Length, true);
}
public void RejectForce(NetDataWriter rejectData)
{
Reject(rejectData.Data, 0, rejectData.Length, true);
}
public void Reject()
{
Reject(null, 0, 0, false);
}
public void Reject(byte[] rejectData)
{
Reject(rejectData, 0, rejectData.Length, false);
}
public void Reject(NetDataWriter rejectData)
{
Reject(rejectData.Data, 0, rejectData.Length, false);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e609855f95e9034889c882b51aaec68
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,272 @@
using System.Net;
using System.Net.Sockets;
using LiteNetLib.Utils;
namespace LiteNetLib
{
/// <summary>
/// Type of message that you receive in OnNetworkReceiveUnconnected event
/// </summary>
public enum UnconnectedMessageType
{
BasicMessage,
Broadcast
}
/// <summary>
/// Disconnect reason that you receive in OnPeerDisconnected event
/// </summary>
public enum DisconnectReason
{
ConnectionFailed,
Timeout,
HostUnreachable,
NetworkUnreachable,
RemoteConnectionClose,
DisconnectPeerCalled,
ConnectionRejected,
InvalidProtocol,
UnknownHost,
Reconnect,
PeerToPeerConnection,
PeerNotFound
}
/// <summary>
/// Additional information about disconnection
/// </summary>
public struct DisconnectInfo
{
/// <summary>
/// Additional info why peer disconnected
/// </summary>
public DisconnectReason Reason;
/// <summary>
/// Error code (if reason is SocketSendError or SocketReceiveError)
/// </summary>
public SocketError SocketErrorCode;
/// <summary>
/// Additional data that can be accessed (only if reason is RemoteConnectionClose)
/// </summary>
public NetPacketReader AdditionalData;
}
public interface INetEventListener
{
/// <summary>
/// New remote peer connected to host, or client connected to remote host
/// </summary>
/// <param name="peer">Connected peer object</param>
void OnPeerConnected(NetPeer peer);
/// <summary>
/// Peer disconnected
/// </summary>
/// <param name="peer">disconnected peer</param>
/// <param name="disconnectInfo">additional info about reason, errorCode or data received with disconnect message</param>
void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);
/// <summary>
/// Network error (on send or receive)
/// </summary>
/// <param name="endPoint">From endPoint (can be null)</param>
/// <param name="socketError">Socket error</param>
void OnNetworkError(IPEndPoint endPoint, SocketError socketError);
/// <summary>
/// Received some data
/// </summary>
/// <param name="peer">From peer</param>
/// <param name="reader">DataReader containing all received data</param>
/// <param name="channelNumber">Number of channel at which packet arrived</param>
/// <param name="deliveryMethod">Type of received packet</param>
void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod);
/// <summary>
/// Received unconnected message
/// </summary>
/// <param name="remoteEndPoint">From address (IP and Port)</param>
/// <param name="reader">Message data</param>
/// <param name="messageType">Message type (simple, discovery request or response)</param>
void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType);
/// <summary>
/// Latency information updated
/// </summary>
/// <param name="peer">Peer with updated latency</param>
/// <param name="latency">latency value in milliseconds</param>
void OnNetworkLatencyUpdate(NetPeer peer, int latency);
/// <summary>
/// On peer connection requested
/// </summary>
/// <param name="request">Request information (EndPoint, internal id, additional data)</param>
void OnConnectionRequest(ConnectionRequest request);
}
public interface IDeliveryEventListener
{
/// <summary>
/// On reliable message delivered
/// </summary>
/// <param name="peer"></param>
/// <param name="userData"></param>
void OnMessageDelivered(NetPeer peer, object userData);
}
public interface INtpEventListener
{
/// <summary>
/// Ntp response
/// </summary>
/// <param name="packet"></param>
void OnNtpResponse(NtpPacket packet);
}
public interface IPeerAddressChangedListener
{
/// <summary>
/// Called when peer address changed (when AllowPeerAddressChange is enabled)
/// </summary>
/// <param name="peer">Peer that changed address (with new address)</param>
/// <param name="previousAddress">previous IP</param>
void OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress);
}
public class EventBasedNetListener : INetEventListener, IDeliveryEventListener, INtpEventListener, IPeerAddressChangedListener
{
public delegate void OnPeerConnected(NetPeer peer);
public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);
public delegate void OnNetworkError(IPEndPoint endPoint, SocketError socketError);
public delegate void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod);
public delegate void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType);
public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency);
public delegate void OnConnectionRequest(ConnectionRequest request);
public delegate void OnDeliveryEvent(NetPeer peer, object userData);
public delegate void OnNtpResponseEvent(NtpPacket packet);
public delegate void OnPeerAddressChangedEvent(NetPeer peer, IPEndPoint previousAddress);
public event OnPeerConnected PeerConnectedEvent;
public event OnPeerDisconnected PeerDisconnectedEvent;
public event OnNetworkError NetworkErrorEvent;
public event OnNetworkReceive NetworkReceiveEvent;
public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent;
public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent;
public event OnConnectionRequest ConnectionRequestEvent;
public event OnDeliveryEvent DeliveryEvent;
public event OnNtpResponseEvent NtpResponseEvent;
public event OnPeerAddressChangedEvent PeerAddressChangedEvent;
public void ClearPeerConnectedEvent()
{
PeerConnectedEvent = null;
}
public void ClearPeerDisconnectedEvent()
{
PeerDisconnectedEvent = null;
}
public void ClearNetworkErrorEvent()
{
NetworkErrorEvent = null;
}
public void ClearNetworkReceiveEvent()
{
NetworkReceiveEvent = null;
}
public void ClearNetworkReceiveUnconnectedEvent()
{
NetworkReceiveUnconnectedEvent = null;
}
public void ClearNetworkLatencyUpdateEvent()
{
NetworkLatencyUpdateEvent = null;
}
public void ClearConnectionRequestEvent()
{
ConnectionRequestEvent = null;
}
public void ClearDeliveryEvent()
{
DeliveryEvent = null;
}
public void ClearNtpResponseEvent()
{
NtpResponseEvent = null;
}
public void ClearPeerAddressChangedEvent()
{
PeerAddressChangedEvent = null;
}
void INetEventListener.OnPeerConnected(NetPeer peer)
{
if (PeerConnectedEvent != null)
PeerConnectedEvent(peer);
}
void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
{
if (PeerDisconnectedEvent != null)
PeerDisconnectedEvent(peer, disconnectInfo);
}
void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode)
{
if (NetworkErrorEvent != null)
NetworkErrorEvent(endPoint, socketErrorCode);
}
void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod)
{
if (NetworkReceiveEvent != null)
NetworkReceiveEvent(peer, reader, channelNumber, deliveryMethod);
}
void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType)
{
if (NetworkReceiveUnconnectedEvent != null)
NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType);
}
void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency)
{
if (NetworkLatencyUpdateEvent != null)
NetworkLatencyUpdateEvent(peer, latency);
}
void INetEventListener.OnConnectionRequest(ConnectionRequest request)
{
if (ConnectionRequestEvent != null)
ConnectionRequestEvent(request);
}
void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData)
{
if (DeliveryEvent != null)
DeliveryEvent(peer, userData);
}
void INtpEventListener.OnNtpResponse(NtpPacket packet)
{
if (NtpResponseEvent != null)
NtpResponseEvent(packet);
}
void IPeerAddressChangedListener.OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress)
{
if (PeerAddressChangedEvent != null)
PeerAddressChangedEvent(peer, previousAddress);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79436ed5864cf48418ac341ea3c70a6b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,133 @@
using System;
using System.Net;
using LiteNetLib.Utils;
namespace LiteNetLib
{
internal sealed class NetConnectRequestPacket
{
public const int HeaderSize = 18;
public readonly long ConnectionTime;
public byte ConnectionNumber;
public readonly byte[] TargetAddress;
public readonly NetDataReader Data;
public readonly int PeerId;
private NetConnectRequestPacket(long connectionTime, byte connectionNumber, int localId, byte[] targetAddress, NetDataReader data)
{
ConnectionTime = connectionTime;
ConnectionNumber = connectionNumber;
TargetAddress = targetAddress;
Data = data;
PeerId = localId;
}
public static int GetProtocolId(NetPacket packet)
{
return BitConverter.ToInt32(packet.RawData, 1);
}
public static NetConnectRequestPacket FromData(NetPacket packet)
{
if (packet.ConnectionNumber >= NetConstants.MaxConnectionNumber)
return null;
//Getting connection time for peer
long connectionTime = BitConverter.ToInt64(packet.RawData, 5);
//Get peer id
int peerId = BitConverter.ToInt32(packet.RawData, 13);
//Get target address
int addrSize = packet.RawData[HeaderSize-1];
if (addrSize != 16 && addrSize != 28)
return null;
byte[] addressBytes = new byte[addrSize];
Buffer.BlockCopy(packet.RawData, HeaderSize, addressBytes, 0, addrSize);
// Read data and create request
var reader = new NetDataReader(null, 0, 0);
if (packet.Size > HeaderSize+addrSize)
reader.SetSource(packet.RawData, HeaderSize + addrSize, packet.Size);
return new NetConnectRequestPacket(connectionTime, packet.ConnectionNumber, peerId, addressBytes, reader);
}
public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectTime, int localId)
{
//Make initial packet
var packet = new NetPacket(PacketProperty.ConnectRequest, connectData.Length+addressBytes.Size);
//Add data
FastBitConverter.GetBytes(packet.RawData, 1, NetConstants.ProtocolId);
FastBitConverter.GetBytes(packet.RawData, 5, connectTime);
FastBitConverter.GetBytes(packet.RawData, 13, localId);
packet.RawData[HeaderSize-1] = (byte)addressBytes.Size;
for (int i = 0; i < addressBytes.Size; i++)
packet.RawData[HeaderSize + i] = addressBytes[i];
Buffer.BlockCopy(connectData.Data, 0, packet.RawData, HeaderSize + addressBytes.Size, connectData.Length);
return packet;
}
}
internal sealed class NetConnectAcceptPacket
{
public const int Size = 15;
public readonly long ConnectionTime;
public readonly byte ConnectionNumber;
public readonly int PeerId;
public readonly bool PeerNetworkChanged;
private NetConnectAcceptPacket(long connectionTime, byte connectionNumber, int peerId, bool peerNetworkChanged)
{
ConnectionTime = connectionTime;
ConnectionNumber = connectionNumber;
PeerId = peerId;
PeerNetworkChanged = peerNetworkChanged;
}
public static NetConnectAcceptPacket FromData(NetPacket packet)
{
if (packet.Size != Size)
return null;
long connectionId = BitConverter.ToInt64(packet.RawData, 1);
//check connect num
byte connectionNumber = packet.RawData[9];
if (connectionNumber >= NetConstants.MaxConnectionNumber)
return null;
//check reused flag
byte isReused = packet.RawData[10];
if (isReused > 1)
return null;
//get remote peer id
int peerId = BitConverter.ToInt32(packet.RawData, 11);
if (peerId < 0)
return null;
return new NetConnectAcceptPacket(connectionId, connectionNumber, peerId, isReused == 1);
}
public static NetPacket Make(long connectTime, byte connectNum, int localPeerId)
{
var packet = new NetPacket(PacketProperty.ConnectAccept, 0);
FastBitConverter.GetBytes(packet.RawData, 1, connectTime);
packet.RawData[9] = connectNum;
FastBitConverter.GetBytes(packet.RawData, 11, localPeerId);
return packet;
}
public static NetPacket MakeNetworkChanged(NetPeer peer)
{
var packet = new NetPacket(PacketProperty.PeerNotFound, Size-1);
FastBitConverter.GetBytes(packet.RawData, 1, peer.ConnectTime);
packet.RawData[9] = peer.ConnectionNum;
packet.RawData[10] = 1;
FastBitConverter.GetBytes(packet.RawData, 11, peer.RemoteId);
return packet;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b8582459906515843a2f2adb010c3fd7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bcd79f96aed490043aa493beed54d929
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,41 @@
using LiteNetLib.Utils;
using System;
using System.Net;
namespace LiteNetLib.Layers
{
public sealed class Crc32cLayer : PacketLayerBase
{
public Crc32cLayer() : base(CRC32C.ChecksumSize)
{
}
public override void ProcessInboundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length)
{
if (length < NetConstants.HeaderSize + CRC32C.ChecksumSize)
{
NetDebug.WriteError("[NM] DataReceived size: bad!");
//Set length to 0 to have netManager drop the packet.
length = 0;
return;
}
int checksumPoint = length - CRC32C.ChecksumSize;
if (CRC32C.Compute(data, offset, checksumPoint) != BitConverter.ToUInt32(data, checksumPoint))
{
NetDebug.Write("[NM] DataReceived checksum: bad!");
//Set length to 0 to have netManager drop the packet.
length = 0;
return;
}
length -= CRC32C.ChecksumSize;
}
public override void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length)
{
FastBitConverter.GetBytes(data, length, CRC32C.Compute(data, offset, length));
length += CRC32C.ChecksumSize;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d52aafc1486ec842a8d133ef41dfd39
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
using System.Net;
namespace LiteNetLib.Layers
{
public abstract class PacketLayerBase
{
public readonly int ExtraPacketSizeForLayer;
protected PacketLayerBase(int extraPacketSizeForLayer)
{
ExtraPacketSizeForLayer = extraPacketSizeForLayer;
}
public abstract void ProcessInboundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length);
public abstract void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bddd0b5590cce7e42918c72429b84bde
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
using System;
using System.Net;
using System.Text;
namespace LiteNetLib.Layers
{
public class XorEncryptLayer : PacketLayerBase
{
private byte[] _byteKey;
public XorEncryptLayer() : base(0)
{
}
public XorEncryptLayer(byte[] key) : this()
{
SetKey(key);
}
public XorEncryptLayer(string key) : this()
{
SetKey(key);
}
public void SetKey(string key)
{
_byteKey = Encoding.UTF8.GetBytes(key);
}
public void SetKey(byte[] key)
{
if (_byteKey == null || _byteKey.Length != key.Length)
_byteKey = new byte[key.Length];
Buffer.BlockCopy(key, 0, _byteKey, 0, key.Length);
}
public override void ProcessInboundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length)
{
if (_byteKey == null)
return;
var cur = offset;
for (var i = 0; i < length; i++, cur++)
{
data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]);
}
}
public override void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length)
{
if (_byteKey == null)
return;
var cur = offset;
for (var i = 0; i < length; i++, cur++)
{
data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb37499ecdd1ce145b0bcaf4b4f0279e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,259 @@
using System.Collections.Concurrent;
using System.Net;
using LiteNetLib.Utils;
namespace LiteNetLib
{
public enum NatAddressType
{
Internal,
External
}
public interface INatPunchListener
{
void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);
void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token);
}
public class EventBasedNatPunchListener : INatPunchListener
{
public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);
public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token);
public event OnNatIntroductionRequest NatIntroductionRequest;
public event OnNatIntroductionSuccess NatIntroductionSuccess;
void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token)
{
if(NatIntroductionRequest != null)
NatIntroductionRequest(localEndPoint, remoteEndPoint, token);
}
void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token)
{
if (NatIntroductionSuccess != null)
NatIntroductionSuccess(targetEndPoint, type, token);
}
}
/// <summary>
/// Module for UDP NAT Hole punching operations. Can be accessed from NetManager
/// </summary>
public sealed class NatPunchModule
{
struct RequestEventData
{
public IPEndPoint LocalEndPoint;
public IPEndPoint RemoteEndPoint;
public string Token;
}
struct SuccessEventData
{
public IPEndPoint TargetEndPoint;
public NatAddressType Type;
public string Token;
}
class NatIntroduceRequestPacket
{
public IPEndPoint Internal { get; set; }
public string Token { get; set; }
}
class NatIntroduceResponsePacket
{
public IPEndPoint Internal { get; set; }
public IPEndPoint External { get; set; }
public string Token { get; set; }
}
class NatPunchPacket
{
public string Token { get; set; }
public bool IsExternal { get; set; }
}
private readonly NetManager _socket;
private readonly ConcurrentQueue<RequestEventData> _requestEvents = new ConcurrentQueue<RequestEventData>();
private readonly ConcurrentQueue<SuccessEventData> _successEvents = new ConcurrentQueue<SuccessEventData>();
private readonly NetDataReader _cacheReader = new NetDataReader();
private readonly NetDataWriter _cacheWriter = new NetDataWriter();
private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(MaxTokenLength);
private INatPunchListener _natPunchListener;
public const int MaxTokenLength = 256;
/// <summary>
/// Events automatically will be called without PollEvents method from another thread
/// </summary>
public bool UnsyncedEvents = false;
internal NatPunchModule(NetManager socket)
{
_socket = socket;
_netPacketProcessor.SubscribeReusable<NatIntroduceResponsePacket>(OnNatIntroductionResponse);
_netPacketProcessor.SubscribeReusable<NatIntroduceRequestPacket, IPEndPoint>(OnNatIntroductionRequest);
_netPacketProcessor.SubscribeReusable<NatPunchPacket, IPEndPoint>(OnNatPunch);
}
internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet)
{
lock (_cacheReader)
{
_cacheReader.SetSource(packet.RawData, NetConstants.HeaderSize, packet.Size);
_netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint);
}
}
public void Init(INatPunchListener listener)
{
_natPunchListener = listener;
}
private void Send<T>(T packet, IPEndPoint target) where T : class, new()
{
_cacheWriter.Reset();
_cacheWriter.Put((byte)PacketProperty.NatMessage);
_netPacketProcessor.Write(_cacheWriter, packet);
_socket.SendRaw(_cacheWriter.Data, 0, _cacheWriter.Length, target);
}
public void NatIntroduce(
IPEndPoint hostInternal,
IPEndPoint hostExternal,
IPEndPoint clientInternal,
IPEndPoint clientExternal,
string additionalInfo)
{
var req = new NatIntroduceResponsePacket
{
Token = additionalInfo
};
//First packet (server) send to client
req.Internal = hostInternal;
req.External = hostExternal;
Send(req, clientExternal);
//Second packet (client) send to server
req.Internal = clientInternal;
req.External = clientExternal;
Send(req, hostExternal);
}
public void PollEvents()
{
if (UnsyncedEvents)
return;
if (_natPunchListener == null || (_successEvents.IsEmpty && _requestEvents.IsEmpty))
return;
while (_successEvents.TryDequeue(out var evt))
{
_natPunchListener.OnNatIntroductionSuccess(
evt.TargetEndPoint,
evt.Type,
evt.Token);
}
while (_requestEvents.TryDequeue(out var evt))
{
_natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token);
}
}
public void SendNatIntroduceRequest(string host, int port, string additionalInfo)
{
SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo);
}
public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo)
{
//prepare outgoing data
string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4);
if (string.IsNullOrEmpty(networkIp))
{
networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6);
}
Send(
new NatIntroduceRequestPacket
{
Internal = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort),
Token = additionalInfo
},
masterServerEndPoint);
}
//We got request and must introduce
private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint)
{
if (UnsyncedEvents)
{
_natPunchListener.OnNatIntroductionRequest(
req.Internal,
senderEndPoint,
req.Token);
}
else
{
_requestEvents.Enqueue(new RequestEventData
{
LocalEndPoint = req.Internal,
RemoteEndPoint = senderEndPoint,
Token = req.Token
});
}
}
//We got introduce and must punch
private void OnNatIntroductionResponse(NatIntroduceResponsePacket req)
{
NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received");
// send internal punch
var punchPacket = new NatPunchPacket {Token = req.Token};
Send(punchPacket, req.Internal);
NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + req.Internal);
// hack for some routers
_socket.Ttl = 2;
_socket.SendRaw(new[] { (byte)PacketProperty.Empty }, 0, 1, req.External);
// send external punch
_socket.Ttl = NetConstants.SocketTTL;
punchPacket.IsExternal = true;
Send(punchPacket, req.External);
NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + req.External);
}
//We got punch and can connect
private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint)
{
//Read info
NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}",
senderEndPoint, req.Token);
//Release punch success to client; enabling him to Connect() to Sender if token is ok
if(UnsyncedEvents)
{
_natPunchListener.OnNatIntroductionSuccess(
senderEndPoint,
req.IsExternal ? NatAddressType.External : NatAddressType.Internal,
req.Token
);
}
else
{
_successEvents.Enqueue(new SuccessEventData
{
TargetEndPoint = senderEndPoint,
Type = req.IsExternal ? NatAddressType.External : NatAddressType.Internal,
Token = req.Token
});
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e608aa88ab820244a90b83eca0f716c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,301 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LiteNetLib
{
internal readonly struct NativeAddr : IEquatable<NativeAddr>
{
//common parts
private readonly long _part1; //family, port, etc
private readonly long _part2;
//ipv6 parts
private readonly long _part3;
private readonly int _part4;
private readonly int _hash;
public NativeAddr(byte[] address, int len)
{
_part1 = BitConverter.ToInt64(address, 0);
_part2 = BitConverter.ToInt64(address, 8);
if (len > 16)
{
_part3 = BitConverter.ToInt64(address, 16);
_part4 = BitConverter.ToInt32(address, 24);
}
else
{
_part3 = 0;
_part4 = 0;
}
_hash = (int)(_part1 >> 32) ^ (int)_part1 ^
(int)(_part2 >> 32) ^ (int)_part2 ^
(int)(_part3 >> 32) ^ (int)_part3 ^
_part4;
}
public override int GetHashCode()
{
return _hash;
}
public bool Equals(NativeAddr other)
{
return _part1 == other._part1 &&
_part2 == other._part2 &&
_part3 == other._part3 &&
_part4 == other._part4;
}
public override bool Equals(object obj)
{
return obj is NativeAddr other && Equals(other);
}
public static bool operator ==(NativeAddr left, NativeAddr right)
{
return left.Equals(right);
}
public static bool operator !=(NativeAddr left, NativeAddr right)
{
return !left.Equals(right);
}
}
internal class NativeEndPoint : IPEndPoint
{
public readonly byte[] NativeAddress;
public NativeEndPoint(byte[] address) : base(IPAddress.Any, 0)
{
NativeAddress = new byte[address.Length];
Buffer.BlockCopy(address, 0, NativeAddress, 0, address.Length);
short family = (short)((address[1] << 8) | address[0]);
Port =(ushort)((address[2] << 8) | address[3]);
if ((NativeSocket.UnixMode && family == NativeSocket.AF_INET6) || (!NativeSocket.UnixMode && (AddressFamily)family == AddressFamily.InterNetworkV6))
{
uint scope = unchecked((uint)(
(address[27] << 24) +
(address[26] << 16) +
(address[25] << 8) +
(address[24])));
#if NETCOREAPP || NETSTANDARD2_1 || NETSTANDARD2_1_OR_GREATER
Address = new IPAddress(new ReadOnlySpan<byte>(address, 8, 16), scope);
#else
byte[] addrBuffer = new byte[16];
Buffer.BlockCopy(address, 8, addrBuffer, 0, 16);
Address = new IPAddress(addrBuffer, scope);
#endif
}
else //IPv4
{
long ipv4Addr = unchecked((uint)((address[4] & 0x000000FF) |
(address[5] << 8 & 0x0000FF00) |
(address[6] << 16 & 0x00FF0000) |
(address[7] << 24)));
Address = new IPAddress(ipv4Addr);
}
}
}
internal static class NativeSocket
{
static
#if LITENETLIB_UNSAFE
unsafe
#endif
class WinSock
{
private const string LibName = "ws2_32.dll";
[DllImport(LibName, SetLastError = true)]
public static extern int recvfrom(
IntPtr socketHandle,
[In, Out] byte[] pinnedBuffer,
[In] int len,
[In] SocketFlags socketFlags,
[Out] byte[] socketAddress,
[In, Out] ref int socketAddressSize);
[DllImport(LibName, SetLastError = true)]
internal static extern int sendto(
IntPtr socketHandle,
#if LITENETLIB_UNSAFE
byte* pinnedBuffer,
#else
[In] byte[] pinnedBuffer,
#endif
[In] int len,
[In] SocketFlags socketFlags,
[In] byte[] socketAddress,
[In] int socketAddressSize);
}
static
#if LITENETLIB_UNSAFE
unsafe
#endif
class UnixSock
{
private const string LibName = "libc";
[DllImport(LibName, SetLastError = true)]
public static extern int recvfrom(
IntPtr socketHandle,
[In, Out] byte[] pinnedBuffer,
[In] int len,
[In] SocketFlags socketFlags,
[Out] byte[] socketAddress,
[In, Out] ref int socketAddressSize);
[DllImport(LibName, SetLastError = true)]
internal static extern int sendto(
IntPtr socketHandle,
#if LITENETLIB_UNSAFE
byte* pinnedBuffer,
#else
[In] byte[] pinnedBuffer,
#endif
[In] int len,
[In] SocketFlags socketFlags,
[In] byte[] socketAddress,
[In] int socketAddressSize);
}
public static readonly bool IsSupported = false;
public static readonly bool UnixMode = false;
public const int IPv4AddrSize = 16;
public const int IPv6AddrSize = 28;
public const int AF_INET = 2;
public const int AF_INET6 = 10;
private static readonly Dictionary<int, SocketError> NativeErrorToSocketError = new Dictionary<int, SocketError>
{
{ 13, SocketError.AccessDenied }, //EACCES
{ 98, SocketError.AddressAlreadyInUse }, //EADDRINUSE
{ 99, SocketError.AddressNotAvailable }, //EADDRNOTAVAIL
{ 97, SocketError.AddressFamilyNotSupported }, //EAFNOSUPPORT
{ 11, SocketError.WouldBlock }, //EAGAIN
{ 114, SocketError.AlreadyInProgress }, //EALREADY
{ 9, SocketError.OperationAborted }, //EBADF
{ 125, SocketError.OperationAborted }, //ECANCELED
{ 103, SocketError.ConnectionAborted }, //ECONNABORTED
{ 111, SocketError.ConnectionRefused }, //ECONNREFUSED
{ 104, SocketError.ConnectionReset }, //ECONNRESET
{ 89, SocketError.DestinationAddressRequired }, //EDESTADDRREQ
{ 14, SocketError.Fault }, //EFAULT
{ 112, SocketError.HostDown }, //EHOSTDOWN
{ 6, SocketError.HostNotFound }, //ENXIO
{ 113, SocketError.HostUnreachable }, //EHOSTUNREACH
{ 115, SocketError.InProgress }, //EINPROGRESS
{ 4, SocketError.Interrupted }, //EINTR
{ 22, SocketError.InvalidArgument }, //EINVAL
{ 106, SocketError.IsConnected }, //EISCONN
{ 24, SocketError.TooManyOpenSockets }, //EMFILE
{ 90, SocketError.MessageSize }, //EMSGSIZE
{ 100, SocketError.NetworkDown }, //ENETDOWN
{ 102, SocketError.NetworkReset }, //ENETRESET
{ 101, SocketError.NetworkUnreachable }, //ENETUNREACH
{ 23, SocketError.TooManyOpenSockets }, //ENFILE
{ 105, SocketError.NoBufferSpaceAvailable }, //ENOBUFS
{ 61, SocketError.NoData }, //ENODATA
{ 2, SocketError.AddressNotAvailable }, //ENOENT
{ 92, SocketError.ProtocolOption }, //ENOPROTOOPT
{ 107, SocketError.NotConnected }, //ENOTCONN
{ 88, SocketError.NotSocket }, //ENOTSOCK
{ 3440, SocketError.OperationNotSupported }, //ENOTSUP
{ 1, SocketError.AccessDenied }, //EPERM
{ 32, SocketError.Shutdown }, //EPIPE
{ 96, SocketError.ProtocolFamilyNotSupported }, //EPFNOSUPPORT
{ 93, SocketError.ProtocolNotSupported }, //EPROTONOSUPPORT
{ 91, SocketError.ProtocolType }, //EPROTOTYPE
{ 94, SocketError.SocketNotSupported }, //ESOCKTNOSUPPORT
{ 108, SocketError.Disconnecting }, //ESHUTDOWN
{ 110, SocketError.TimedOut }, //ETIMEDOUT
{ 0, SocketError.Success }
};
static NativeSocket()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
IsSupported = true;
UnixMode = true;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
IsSupported = true;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int RecvFrom(
IntPtr socketHandle,
byte[] pinnedBuffer,
int len,
byte[] socketAddress,
ref int socketAddressSize)
{
return UnixMode
? UnixSock.recvfrom(socketHandle, pinnedBuffer, len, 0, socketAddress, ref socketAddressSize)
: WinSock.recvfrom(socketHandle, pinnedBuffer, len, 0, socketAddress, ref socketAddressSize);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public
#if LITENETLIB_UNSAFE
unsafe
#endif
static int SendTo(
IntPtr socketHandle,
#if LITENETLIB_UNSAFE
byte* pinnedBuffer,
#else
byte[] pinnedBuffer,
#endif
int len,
byte[] socketAddress,
int socketAddressSize)
{
return UnixMode
? UnixSock.sendto(socketHandle, pinnedBuffer, len, 0, socketAddress, socketAddressSize)
: WinSock.sendto(socketHandle, pinnedBuffer, len, 0, socketAddress, socketAddressSize);
}
public static SocketError GetSocketError()
{
int error = Marshal.GetLastWin32Error();
if (UnixMode)
return NativeErrorToSocketError.TryGetValue(error, out var err)
? err
: SocketError.SocketError;
return (SocketError)error;
}
public static SocketException GetSocketException()
{
int error = Marshal.GetLastWin32Error();
if (UnixMode)
return NativeErrorToSocketError.TryGetValue(error, out var err)
? new SocketException((int)err)
: new SocketException((int)SocketError.SocketError);
return new SocketException(error);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint)
{
return UnixMode
? (short)(remoteEndPoint.AddressFamily == AddressFamily.InterNetwork ? AF_INET : AF_INET6)
: (short)remoteEndPoint.AddressFamily;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12cfcf4490f2b8f4db979ee833ecf5af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,75 @@
namespace LiteNetLib
{
/// <summary>
/// Sending method type
/// </summary>
public enum DeliveryMethod : byte
{
/// <summary>
/// Unreliable. Packets can be dropped, can be duplicated, can arrive without order.
/// </summary>
Unreliable = 4,
/// <summary>
/// Reliable. Packets won't be dropped, won't be duplicated, can arrive without order.
/// </summary>
ReliableUnordered = 0,
/// <summary>
/// Unreliable. Packets can be dropped, won't be duplicated, will arrive in order.
/// </summary>
Sequenced = 1,
/// <summary>
/// Reliable and ordered. Packets won't be dropped, won't be duplicated, will arrive in order.
/// </summary>
ReliableOrdered = 2,
/// <summary>
/// Reliable only last packet. Packets can be dropped (except the last one), won't be duplicated, will arrive in order.
/// Cannot be fragmented
/// </summary>
ReliableSequenced = 3
}
/// <summary>
/// Network constants. Can be tuned from sources for your purposes.
/// </summary>
public static class NetConstants
{
//can be tuned
public const int DefaultWindowSize = 64;
public const int SocketBufferSize = 1024 * 1024; //1mb
public const int SocketTTL = 255;
public const int HeaderSize = 1;
public const int ChanneledHeaderSize = 4;
public const int FragmentHeaderSize = 6;
public const int FragmentedHeaderTotalSize = ChanneledHeaderSize + FragmentHeaderSize;
public const ushort MaxSequence = 32768;
public const ushort HalfMaxSequence = MaxSequence / 2;
//protocol
internal const int ProtocolId = 13;
internal const int MaxUdpHeaderSize = 68;
internal const int ChannelTypeCount = 4;
internal static readonly int[] PossibleMtu =
{
576 - MaxUdpHeaderSize, //minimal (RFC 1191)
1024, //most games standard
1232 - MaxUdpHeaderSize,
1460 - MaxUdpHeaderSize, //google cloud
1472 - MaxUdpHeaderSize, //VPN
1492 - MaxUdpHeaderSize, //Ethernet with LLC and SNAP, PPPoE (RFC 1042)
1500 - MaxUdpHeaderSize //Ethernet II (RFC 1191)
};
//Max possible single packet size
public static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1];
public static readonly int MaxUnreliableDataSize = MaxPacketSize - HeaderSize;
//peer specific
public const byte MaxConnectionNumber = 4;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b42ee5a523e67ff4c9149f91f7fe4245
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,92 @@
using System;
using System.Diagnostics;
namespace LiteNetLib
{
public class InvalidPacketException : ArgumentException
{
public InvalidPacketException(string message) : base(message)
{
}
}
public class TooBigPacketException : InvalidPacketException
{
public TooBigPacketException(string message) : base(message)
{
}
}
public enum NetLogLevel
{
Warning,
Error,
Trace,
Info
}
/// <summary>
/// Interface to implement for your own logger
/// </summary>
public interface INetLogger
{
void WriteNet(NetLogLevel level, string str, params object[] args);
}
/// <summary>
/// Static class for defining your own LiteNetLib logger instead of Console.WriteLine
/// or Debug.Log if compiled with UNITY flag
/// </summary>
public static class NetDebug
{
public static INetLogger Logger = null;
private static readonly object DebugLogLock = new object();
private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args)
{
lock (DebugLogLock)
{
if (Logger == null)
{
#if UNITY_5_3_OR_NEWER
UnityEngine.Debug.Log(string.Format(str, args));
#else
Console.WriteLine(str, args);
#endif
}
else
{
Logger.WriteNet(logLevel, str, args);
}
}
}
[Conditional("DEBUG_MESSAGES")]
internal static void Write(string str, params object[] args)
{
WriteLogic(NetLogLevel.Trace, str, args);
}
[Conditional("DEBUG_MESSAGES")]
internal static void Write(NetLogLevel level, string str, params object[] args)
{
WriteLogic(level, str, args);
}
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")]
internal static void WriteForce(string str, params object[] args)
{
WriteLogic(NetLogLevel.Trace, str, args);
}
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")]
internal static void WriteForce(NetLogLevel level, string str, params object[] args)
{
WriteLogic(level, str, args);
}
internal static void WriteError(string str, params object[] args)
{
WriteLogic(NetLogLevel.Error, str, args);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f50ae51c124bf1439e339eee1fcd6f5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,80 @@
using System;
using System.Threading;
namespace LiteNetLib
{
public partial class NetManager
{
private NetPacket _poolHead;
private int _poolCount;
private readonly object _poolLock = new object();
/// <summary>
/// Maximum packet pool size (increase if you have tons of packets sending)
/// </summary>
public int PacketPoolSize = 1000;
public int PoolCount => _poolCount;
private NetPacket PoolGetWithData(PacketProperty property, byte[] data, int start, int length)
{
int headerSize = NetPacket.GetHeaderSize(property);
NetPacket packet = PoolGetPacket(length + headerSize);
packet.Property = property;
Buffer.BlockCopy(data, start, packet.RawData, headerSize, length);
return packet;
}
//Get packet with size
private NetPacket PoolGetWithProperty(PacketProperty property, int size)
{
NetPacket packet = PoolGetPacket(size + NetPacket.GetHeaderSize(property));
packet.Property = property;
return packet;
}
private NetPacket PoolGetWithProperty(PacketProperty property)
{
NetPacket packet = PoolGetPacket(NetPacket.GetHeaderSize(property));
packet.Property = property;
return packet;
}
internal NetPacket PoolGetPacket(int size)
{
NetPacket packet;
lock (_poolLock)
{
packet = _poolHead;
if (packet == null)
return new NetPacket(size);
_poolHead = _poolHead.Next;
_poolCount--;
}
packet.Size = size;
if (packet.RawData.Length < size)
packet.RawData = new byte[size];
return packet;
}
internal void PoolRecycle(NetPacket packet)
{
if (packet.RawData.Length > NetConstants.MaxPacketSize || _poolCount >= PacketPoolSize)
{
//Don't pool big packets. Save memory
return;
}
//Clean fragmented flag
packet.RawData[0] = 0;
lock (_poolLock)
{
packet.Next = _poolHead;
_poolHead = packet;
_poolCount++;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d384bada29340e542945f001cad348ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,701 @@
using System.Runtime.InteropServices;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using LiteNetLib.Utils;
namespace LiteNetLib
{
public partial class NetManager
{
public bool SocketActive(bool ipv4)
{
if (ipv4)
{
if (_udpSocketv4 != null)
return _udpSocketv4.Connected;
return false;
}
else
{
if (_udpSocketv6 != null)
return _udpSocketv6.Connected;
return false;
}
}
private const int ReceivePollingTime = 500000; //0.5 second
private Socket _udpSocketv4;
private Socket _udpSocketv6;
private Thread _threadv4;
private Thread _threadv6;
private IPEndPoint _bufferEndPointv4;
private IPEndPoint _bufferEndPointv6;
private PausedSocketFix _pausedSocketFix;
#if !LITENETLIB_UNSAFE
[ThreadStatic] private static byte[] _sendToBuffer;
#endif
[ThreadStatic] private static byte[] _endPointBuffer;
private readonly Dictionary<NativeAddr, IPEndPoint> _nativeAddrMap = new Dictionary<NativeAddr, IPEndPoint>();
private const int SioUdpConnreset = -1744830452; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse("ff02::1");
public static readonly bool IPv6Support;
/// <summary>
/// Maximum packets count that will be processed in Manual PollEvents
/// </summary>
public int MaxPacketsReceivePerUpdate = 0;
public short Ttl
{
get
{
#if UNITY_SWITCH
return 0;
#else
return _udpSocketv4.Ttl;
#endif
}
internal set
{
#if !UNITY_SWITCH
_udpSocketv4.Ttl = value;
#endif
}
}
static NetManager()
{
#if DISABLE_IPV6
IPv6Support = false;
#elif !UNITY_2019_1_OR_NEWER && !UNITY_2018_4_OR_NEWER && (!UNITY_EDITOR && ENABLE_IL2CPP)
string version = UnityEngine.Application.unityVersion;
IPv6Support = Socket.OSSupportsIPv6 && int.Parse(version.Remove(version.IndexOf('f')).Split('.')[2]) >= 6;
#else
IPv6Support = Socket.OSSupportsIPv6;
#endif
}
private bool IsActive()
{
return IsRunning;
}
private void RegisterEndPoint(IPEndPoint ep)
{
if (UseNativeSockets && ep is NativeEndPoint nep)
{
_nativeAddrMap.Add(new NativeAddr(nep.NativeAddress, nep.NativeAddress.Length), nep);
}
}
private void UnregisterEndPoint(IPEndPoint ep)
{
if (UseNativeSockets && ep is NativeEndPoint nep)
{
var nativeAddr = new NativeAddr(nep.NativeAddress, nep.NativeAddress.Length);
_nativeAddrMap.Remove(nativeAddr);
}
}
private bool ProcessError(SocketException ex)
{
switch (ex.SocketErrorCode)
{
#if UNITY_IOS && !UNITY_EDITOR
case SocketError.NotConnected:
#endif
case SocketError.Interrupted:
case SocketError.NotSocket:
case SocketError.OperationAborted:
return true;
case SocketError.ConnectionReset:
case SocketError.MessageSize:
case SocketError.TimedOut:
case SocketError.NetworkReset:
//NetDebug.Write($"[R]Ignored error: {(int)ex.SocketErrorCode} - {ex}");
break;
default:
NetDebug.WriteError($"[R]Error code: {(int)ex.SocketErrorCode} - {ex}");
CreateEvent(NetEvent.EType.Error, errorCode: ex.SocketErrorCode);
break;
}
return false;
}
private void ManualReceive(Socket socket, EndPoint bufferEndPoint)
{
//Reading data
try
{
int packetsReceived = 0;
while (socket.Available > 0)
{
var packet = PoolGetPacket(NetConstants.MaxPacketSize);
packet.Size = socket.ReceiveFrom(packet.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None,
ref bufferEndPoint);
//NetDebug.Write(NetLogLevel.Trace, $"[R]Received data from {bufferEndPoint}, result: {packet.Size}");
OnMessageReceived(packet, (IPEndPoint)bufferEndPoint);
packetsReceived++;
if (packetsReceived == MaxPacketsReceivePerUpdate)
break;
}
}
catch (SocketException ex)
{
ProcessError(ex);
}
catch (ObjectDisposedException)
{
}
catch (Exception e)
{
//protects socket receive thread
NetDebug.WriteError("[NM] SocketReceiveThread error: " + e);
}
}
private void NativeReceiveLogic(object state)
{
Socket socket = (Socket)state;
IntPtr socketHandle = socket.Handle;
byte[] addrBuffer = new byte[socket.AddressFamily == AddressFamily.InterNetwork
? NativeSocket.IPv4AddrSize
: NativeSocket.IPv6AddrSize];
int addrSize = addrBuffer.Length;
NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize);
while (IsActive())
{
//Reading data
packet.Size = NativeSocket.RecvFrom(socketHandle, packet.RawData, NetConstants.MaxPacketSize, addrBuffer, ref addrSize);
if (packet.Size == 0)
return;
if (packet.Size == -1)
{
SocketError errorCode = NativeSocket.GetSocketError();
if (errorCode == SocketError.WouldBlock || errorCode == SocketError.TimedOut) //Linux timeout EAGAIN
continue;
if (ProcessError(new SocketException((int)errorCode)))
return;
continue;
}
NativeAddr nativeAddr = new NativeAddr(addrBuffer, addrSize);
if (!_nativeAddrMap.TryGetValue(nativeAddr, out var endPoint))
endPoint = new NativeEndPoint(addrBuffer);
//All ok!
//NetDebug.WriteForce($"[R]Received data from {endPoint}, result: {packet.Size}");
OnMessageReceived(packet, endPoint);
packet = PoolGetPacket(NetConstants.MaxPacketSize);
}
}
private void ReceiveLogic(object state)
{
Socket socket = (Socket)state;
EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0);
while (IsActive())
{
//Reading data
try
{
if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead))
continue;
NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize);
packet.Size = socket.ReceiveFrom(packet.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None,
ref bufferEndPoint);
//NetDebug.Write(NetLogLevel.Trace, $"[R]Received data from {bufferEndPoint}, result: {packet.Size}");
OnMessageReceived(packet, (IPEndPoint)bufferEndPoint);
}
catch (SocketException ex)
{
if (ProcessError(ex))
return;
}
catch (ObjectDisposedException)
{
//socket closed
return;
}
catch (ThreadAbortException)
{
//thread closed
return;
}
catch (Exception e)
{
//protects socket receive thread
NetDebug.WriteError("[NM] SocketReceiveThread error: " + e);
}
}
}
/// <summary>
/// Start logic thread and listening on selected port
/// </summary>
/// <param name="addressIPv4">bind to specific ipv4 address</param>
/// <param name="addressIPv6">bind to specific ipv6 address</param>
/// <param name="port">port to listen</param>
/// <param name="manualMode">mode of library</param>
public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool manualMode)
{
if (IsRunning && !IsActive())
return false;
_manualMode = manualMode;
UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported;
//osx doesn't support dual mode
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && IPv6Mode == IPv6Mode.DualMode)
IPv6Mode = IPv6Mode.SeparateSocket;
bool dualMode = IPv6Mode == IPv6Mode.DualMode && IPv6Support;
_udpSocketv4 = new Socket(
dualMode ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
if (!BindSocket(_udpSocketv4, new IPEndPoint(dualMode ? addressIPv6 : addressIPv4, port)))
return false;
LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port;
if (_pausedSocketFix == null)
_pausedSocketFix = new PausedSocketFix(this, addressIPv4, addressIPv6, port, manualMode);
if (dualMode)
_udpSocketv6 = _udpSocketv4;
IsRunning = true;
if (!_manualMode)
{
ParameterizedThreadStart ts = ReceiveLogic;
if (UseNativeSockets)
ts = NativeReceiveLogic;
_threadv4 = new Thread(ts)
{
Name = $"SocketThreadv4({LocalPort})",
IsBackground = true
};
_threadv4.Start(_udpSocketv4);
_logicThread = new Thread(UpdateLogic) { Name = "LogicThread", IsBackground = true };
_logicThread.Start();
}
else
{
_bufferEndPointv4 = new IPEndPoint(IPAddress.Any, 0);
}
//Check IPv6 support
if (IPv6Support && IPv6Mode == IPv6Mode.SeparateSocket)
{
_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
//Use one port for two sockets
if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort)))
{
if (_manualMode)
{
_bufferEndPointv6 = new IPEndPoint(IPAddress.IPv6Any, 0);
}
else
{
ParameterizedThreadStart ts = ReceiveLogic;
if (UseNativeSockets)
ts = NativeReceiveLogic;
_threadv6 = new Thread(ts)
{
Name = $"SocketThreadv6({LocalPort})",
IsBackground = true
};
_threadv6.Start(_udpSocketv6);
}
}
}
return true;
}
private bool BindSocket(Socket socket, IPEndPoint ep)
{
//Setup socket
socket.ReceiveTimeout = 500;
socket.SendTimeout = 500;
socket.ReceiveBufferSize = NetConstants.SocketBufferSize;
socket.SendBufferSize = NetConstants.SocketBufferSize;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
socket.IOControl(SioUdpConnreset, new byte[] { 0 }, null);
}
catch
{
//ignored
}
}
try
{
socket.ExclusiveAddressUse = !ReuseAddress;
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, ReuseAddress);
}
catch
{
//Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it
}
if (ep.AddressFamily == AddressFamily.InterNetwork || IPv6Mode == IPv6Mode.DualMode)
{
Ttl = NetConstants.SocketTTL;
try { socket.EnableBroadcast = true; }
catch (SocketException e)
{
NetDebug.WriteError($"[B]Broadcast error: {e.SocketErrorCode}");
}
if (IPv6Mode == IPv6Mode.DualMode)
{
try { socket.DualMode = true; }
catch (Exception e)
{
NetDebug.WriteError($"[B]Bind exception (dualmode setting): {e}");
}
}
else if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
try { socket.DontFragment = true; }
catch (SocketException e)
{
NetDebug.WriteError($"[B]DontFragment error: {e.SocketErrorCode}");
}
}
}
//Bind
try
{
socket.Bind(ep);
NetDebug.Write(NetLogLevel.Trace, $"[B]Successfully binded to port: {((IPEndPoint)socket.LocalEndPoint).Port}, AF: {socket.AddressFamily}");
//join multicast
if (ep.AddressFamily == AddressFamily.InterNetworkV6)
{
try
{
#if !UNITY_2018_3_OR_NEWER
socket.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.AddMembership,
new IPv6MulticastOption(MulticastAddressV6));
#endif
}
catch (Exception)
{
// Unity3d throws exception - ignored
}
}
}
catch (SocketException bindException)
{
switch (bindException.SocketErrorCode)
{
//IPv6 bind fix
case SocketError.AddressAlreadyInUse:
if (socket.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Mode != IPv6Mode.DualMode)
{
try
{
//Set IPv6Only
socket.DualMode = false;
socket.Bind(ep);
}
catch (SocketException ex)
{
//because its fixed in 2018_3
NetDebug.WriteError($"[B]Bind exception: {ex}, errorCode: {ex.SocketErrorCode}");
return false;
}
return true;
}
break;
//hack for iOS (Unity3D)
case SocketError.AddressFamilyNotSupported:
return true;
}
NetDebug.WriteError($"[B]Bind exception: {bindException}, errorCode: {bindException.SocketErrorCode}");
return false;
}
return true;
}
internal int SendRawAndRecycle(NetPacket packet, IPEndPoint remoteEndPoint)
{
int result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
PoolRecycle(packet);
return result;
}
internal int SendRaw(NetPacket packet, IPEndPoint remoteEndPoint)
{
return SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
}
internal int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint)
{
if (!IsRunning)
return 0;
NetPacket expandedPacket = null;
if (_extraPacketLayer != null)
{
expandedPacket = PoolGetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer);
Buffer.BlockCopy(message, start, expandedPacket.RawData, 0, length);
start = 0;
_extraPacketLayer.ProcessOutBoundPacket(ref remoteEndPoint, ref expandedPacket.RawData, ref start, ref length);
message = expandedPacket.RawData;
}
var socket = _udpSocketv4;
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support)
{
socket = _udpSocketv6;
if (socket == null)
return 0;
}
int result;
try
{
if (UseNativeSockets)
{
byte[] socketAddress;
if (remoteEndPoint is NativeEndPoint nep)
{
socketAddress = nep.NativeAddress;
}
else //Convert endpoint to raw
{
if (_endPointBuffer == null)
_endPointBuffer = new byte[NativeSocket.IPv6AddrSize];
socketAddress = _endPointBuffer;
bool ipv4 = remoteEndPoint.AddressFamily == AddressFamily.InterNetwork;
short addressFamily = NativeSocket.GetNativeAddressFamily(remoteEndPoint);
socketAddress[0] = (byte)(addressFamily);
socketAddress[1] = (byte)(addressFamily >> 8);
socketAddress[2] = (byte)(remoteEndPoint.Port >> 8);
socketAddress[3] = (byte)(remoteEndPoint.Port);
if (ipv4)
{
#pragma warning disable 618
long addr = remoteEndPoint.Address.Address;
#pragma warning restore 618
socketAddress[4] = (byte)(addr);
socketAddress[5] = (byte)(addr >> 8);
socketAddress[6] = (byte)(addr >> 16);
socketAddress[7] = (byte)(addr >> 24);
}
else
{
#if NETCOREAPP || NETSTANDARD2_1 || NETSTANDARD2_1_OR_GREATER
remoteEndPoint.Address.TryWriteBytes(new Span<byte>(socketAddress, 8, 16), out _);
#else
byte[] addrBytes = remoteEndPoint.Address.GetAddressBytes();
Buffer.BlockCopy(addrBytes, 0, socketAddress, 8, 16);
#endif
}
}
#if LITENETLIB_UNSAFE
unsafe
{
fixed (byte* dataWithOffset = &message[start])
{
result =
NativeSocket.SendTo(socket.Handle, dataWithOffset, length, socketAddress, socketAddress.Length);
}
}
#else
if (start > 0)
{
if (_sendToBuffer == null)
_sendToBuffer = new byte[NetConstants.MaxPacketSize];
Buffer.BlockCopy(message, start, _sendToBuffer, 0, length);
message = _sendToBuffer;
}
result = NativeSocket.SendTo(socket.Handle, message, length, socketAddress, socketAddress.Length);
#endif
if (result == -1)
throw NativeSocket.GetSocketException();
}
else
{
result = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint);
}
//NetDebug.WriteForce("[S]Send packet to {0}, result: {1}", remoteEndPoint, result);
}
catch (SocketException ex)
{
switch (ex.SocketErrorCode)
{
case SocketError.NoBufferSpaceAvailable:
case SocketError.Interrupted:
return 0;
case SocketError.MessageSize:
NetDebug.Write(NetLogLevel.Trace, "[SRD] 10040, datalen: {0}", length);
return 0;
case SocketError.HostUnreachable:
case SocketError.NetworkUnreachable:
if (DisconnectOnUnreachable && TryGetPeer(remoteEndPoint, out var fromPeer))
{
DisconnectPeerForce(
fromPeer,
ex.SocketErrorCode == SocketError.HostUnreachable
? DisconnectReason.HostUnreachable
: DisconnectReason.NetworkUnreachable,
ex.SocketErrorCode,
null);
}
CreateEvent(NetEvent.EType.Error, remoteEndPoint: remoteEndPoint, errorCode: ex.SocketErrorCode);
return -1;
default:
NetDebug.WriteError($"[S] {ex}");
return -1;
}
}
catch (Exception ex)
{
NetDebug.WriteError($"[S] {ex}");
return 0;
}
finally
{
if (expandedPacket != null)
{
PoolRecycle(expandedPacket);
}
}
if (result <= 0)
return 0;
if (EnableStatistics)
{
Statistics.IncrementPacketsSent();
Statistics.AddBytesSent(length);
}
return result;
}
public bool SendBroadcast(NetDataWriter writer, int port)
{
return SendBroadcast(writer.Data, 0, writer.Length, port);
}
public bool SendBroadcast(byte[] data, int port)
{
return SendBroadcast(data, 0, data.Length, port);
}
public bool SendBroadcast(byte[] data, int start, int length, int port)
{
if (!IsActive())
return false;
NetPacket packet;
if (_extraPacketLayer != null)
{
var headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast);
packet = PoolGetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer);
packet.Property = PacketProperty.Broadcast;
Buffer.BlockCopy(data, start, packet.RawData, headerSize, length);
var checksumComputeStart = 0;
int preCrcLength = length + headerSize;
IPEndPoint emptyEp = null;
_extraPacketLayer.ProcessOutBoundPacket(ref emptyEp, ref packet.RawData, ref checksumComputeStart, ref preCrcLength);
}
else
{
packet = PoolGetWithData(PacketProperty.Broadcast, data, start, length);
}
bool broadcastSuccess = false;
bool multicastSuccess = false;
try
{
broadcastSuccess = _udpSocketv4.SendTo(
packet.RawData,
0,
packet.Size,
SocketFlags.None,
new IPEndPoint(IPAddress.Broadcast, port)) > 0;
if (_udpSocketv6 != null)
{
multicastSuccess = _udpSocketv6.SendTo(
packet.RawData,
0,
packet.Size,
SocketFlags.None,
new IPEndPoint(MulticastAddressV6, port)) > 0;
}
}
catch (Exception ex)
{
NetDebug.WriteError($"[S][MCAST] {ex}");
return broadcastSuccess;
}
finally
{
PoolRecycle(packet);
}
return broadcastSuccess || multicastSuccess;
}
internal void CloseSocket(bool suspend)
{
if (!suspend)
IsRunning = false;
//cleanup dual mode
if (_udpSocketv4 == _udpSocketv6)
_udpSocketv6 = null;
_udpSocketv4?.Close();
_udpSocketv6?.Close();
_udpSocketv4 = null;
_udpSocketv6 = null;
if (_threadv4 != null && _threadv4 != Thread.CurrentThread)
_threadv4.Join();
if (_threadv6 != null && _threadv6 != Thread.CurrentThread)
_threadv6.Join();
_threadv4 = null;
_threadv6 = null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9cc0423407df3e74a8e5a15dbbad2598
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37d95580df7122c44b9333cd3ab77732
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,161 @@
using System;
using System.Net;
using LiteNetLib.Utils;
namespace LiteNetLib
{
internal enum PacketProperty : byte
{
Unreliable,
Channeled,
Ack,
Ping,
Pong,
ConnectRequest,
ConnectAccept,
Disconnect,
UnconnectedMessage,
MtuCheck,
MtuOk,
Broadcast,
Merged,
ShutdownOk,
PeerNotFound,
InvalidProtocol,
NatMessage,
Empty
}
internal sealed class NetPacket
{
private static readonly int PropertiesCount = Enum.GetValues(typeof(PacketProperty)).Length;
private static readonly int[] HeaderSizes;
static NetPacket()
{
HeaderSizes = NetUtils.AllocatePinnedUninitializedArray<int>(PropertiesCount);
for (int i = 0; i < HeaderSizes.Length; i++)
{
switch ((PacketProperty)i)
{
case PacketProperty.Channeled:
case PacketProperty.Ack:
HeaderSizes[i] = NetConstants.ChanneledHeaderSize;
break;
case PacketProperty.Ping:
HeaderSizes[i] = NetConstants.HeaderSize + 2;
break;
case PacketProperty.ConnectRequest:
HeaderSizes[i] = NetConnectRequestPacket.HeaderSize;
break;
case PacketProperty.ConnectAccept:
HeaderSizes[i] = NetConnectAcceptPacket.Size;
break;
case PacketProperty.Disconnect:
HeaderSizes[i] = NetConstants.HeaderSize + 8;
break;
case PacketProperty.Pong:
HeaderSizes[i] = NetConstants.HeaderSize + 10;
break;
default:
HeaderSizes[i] = NetConstants.HeaderSize;
break;
}
}
}
//Header
public PacketProperty Property
{
get => (PacketProperty)(RawData[0] & 0x1F);
set => RawData[0] = (byte)((RawData[0] & 0xE0) | (byte)value);
}
public byte ConnectionNumber
{
get => (byte)((RawData[0] & 0x60) >> 5);
set => RawData[0] = (byte) ((RawData[0] & 0x9F) | (value << 5));
}
public ushort Sequence
{
get => BitConverter.ToUInt16(RawData, 1);
set => FastBitConverter.GetBytes(RawData, 1, value);
}
public bool IsFragmented => (RawData[0] & 0x80) != 0;
public void MarkFragmented()
{
RawData[0] |= 0x80; //set first bit
}
public byte ChannelId
{
get => RawData[3];
set => RawData[3] = value;
}
public ushort FragmentId
{
get => BitConverter.ToUInt16(RawData, 4);
set => FastBitConverter.GetBytes(RawData, 4, value);
}
public ushort FragmentPart
{
get => BitConverter.ToUInt16(RawData, 6);
set => FastBitConverter.GetBytes(RawData, 6, value);
}
public ushort FragmentsTotal
{
get => BitConverter.ToUInt16(RawData, 8);
set => FastBitConverter.GetBytes(RawData, 8, value);
}
//Data
public byte[] RawData;
public int Size;
//Delivery
public object UserData;
//Pool node
public NetPacket Next;
public NetPacket(int size)
{
RawData = new byte[size];
Size = size;
}
public NetPacket(PacketProperty property, int size)
{
size += GetHeaderSize(property);
RawData = new byte[size];
Property = property;
Size = size;
}
public static int GetHeaderSize(PacketProperty property)
{
return HeaderSizes[(int)property];
}
public int GetHeaderSize()
{
return HeaderSizes[RawData[0] & 0x1F];
}
public bool Verify()
{
byte property = (byte)(RawData[0] & 0x1F);
if (property >= PropertiesCount)
return false;
int headerSize = HeaderSizes[property];
bool fragmented = (RawData[0] & 0x80) != 0;
return Size >= headerSize && (!fragmented || Size >= headerSize + NetConstants.FragmentHeaderSize);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e7ddd322169be074f870f73db1a55255
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e1a9277334c51545b92369c8e4c6d74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,81 @@
using System.Threading;
namespace LiteNetLib
{
public sealed class NetStatistics
{
private long _packetsSent;
private long _packetsReceived;
private long _bytesSent;
private long _bytesReceived;
private long _packetLoss;
public long PacketsSent => Interlocked.Read(ref _packetsSent);
public long PacketsReceived => Interlocked.Read(ref _packetsReceived);
public long BytesSent => Interlocked.Read(ref _bytesSent);
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
public long PacketLoss => Interlocked.Read(ref _packetLoss);
public long PacketLossPercent
{
get
{
long sent = PacketsSent, loss = PacketLoss;
return sent == 0 ? 0 : loss * 100 / sent;
}
}
public void Reset()
{
Interlocked.Exchange(ref _packetsSent, 0);
Interlocked.Exchange(ref _packetsReceived, 0);
Interlocked.Exchange(ref _bytesSent, 0);
Interlocked.Exchange(ref _bytesReceived, 0);
Interlocked.Exchange(ref _packetLoss, 0);
}
public void IncrementPacketsSent()
{
Interlocked.Increment(ref _packetsSent);
}
public void IncrementPacketsReceived()
{
Interlocked.Increment(ref _packetsReceived);
}
public void AddBytesSent(long bytesSent)
{
Interlocked.Add(ref _bytesSent, bytesSent);
}
public void AddBytesReceived(long bytesReceived)
{
Interlocked.Add(ref _bytesReceived, bytesReceived);
}
public void IncrementPacketLoss()
{
Interlocked.Increment(ref _packetLoss);
}
public void AddPacketLoss(long packetLoss)
{
Interlocked.Add(ref _packetLoss, packetLoss);
}
public override string ToString()
{
return
string.Format(
"BytesReceived: {0}\nPacketsReceived: {1}\nBytesSent: {2}\nPacketsSent: {3}\nPacketLoss: {4}\nPacketLossPercent: {5}\n",
BytesReceived,
PacketsReceived,
BytesSent,
PacketsSent,
PacketLoss,
PacketLossPercent);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15d4b62077bda58428b77d57fa4a9288
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
namespace LiteNetLib
{
/// <summary>
/// Address type that you want to receive from NetUtils.GetLocalIp method
/// </summary>
[Flags]
public enum LocalAddrType
{
IPv4 = 1,
IPv6 = 2,
All = IPv4 | IPv6
}
/// <summary>
/// Some specific network utilities
/// </summary>
public static class NetUtils
{
public static IPEndPoint MakeEndPoint(string hostStr, int port)
{
return new IPEndPoint(ResolveAddress(hostStr), port);
}
public static IPAddress ResolveAddress(string hostStr)
{
if(hostStr == "localhost")
return IPAddress.Loopback;
if (!IPAddress.TryParse(hostStr, out var ipAddress))
{
if (NetManager.IPv6Support)
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetworkV6);
if (ipAddress == null)
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetwork);
}
if (ipAddress == null)
throw new ArgumentException("Invalid address: " + hostStr);
return ipAddress;
}
public static IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily)
{
IPAddress[] addresses = Dns.GetHostEntry(hostStr).AddressList;
foreach (IPAddress ip in addresses)
{
if (ip.AddressFamily == addressFamily)
{
return ip;
}
}
return null;
}
/// <summary>
/// Get all local ip addresses
/// </summary>
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
/// <returns>List with all local ip addresses</returns>
public static List<string> GetLocalIpList(LocalAddrType addrType)
{
List<string> targetList = new List<string>();
GetLocalIpList(targetList, addrType);
return targetList;
}
/// <summary>
/// Get all local ip addresses (non alloc version)
/// </summary>
/// <param name="targetList">result list</param>
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
public static void GetLocalIpList(IList<string> targetList, LocalAddrType addrType)
{
bool ipv4 = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4;
bool ipv6 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6;
try
{
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
{
//Skip loopback and disabled network interfaces
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback ||
ni.OperationalStatus != OperationalStatus.Up)
continue;
var ipProps = ni.GetIPProperties();
//Skip address without gateway
if (ipProps.GatewayAddresses.Count == 0)
continue;
foreach (UnicastIPAddressInformation ip in ipProps.UnicastAddresses)
{
var address = ip.Address;
if ((ipv4 && address.AddressFamily == AddressFamily.InterNetwork) ||
(ipv6 && address.AddressFamily == AddressFamily.InterNetworkV6))
targetList.Add(address.ToString());
}
}
//Fallback mode (unity android)
if (targetList.Count == 0)
{
IPAddress[] addresses = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
foreach (IPAddress ip in addresses)
{
if((ipv4 && ip.AddressFamily == AddressFamily.InterNetwork) ||
(ipv6 && ip.AddressFamily == AddressFamily.InterNetworkV6))
targetList.Add(ip.ToString());
}
}
}
catch
{
//ignored
}
if (targetList.Count == 0)
{
if(ipv4)
targetList.Add("127.0.0.1");
if(ipv6)
targetList.Add("::1");
}
}
private static readonly List<string> IpList = new List<string>();
/// <summary>
/// Get first detected local ip address
/// </summary>
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
/// <returns>IP address if available. Else - string.Empty</returns>
public static string GetLocalIp(LocalAddrType addrType)
{
lock (IpList)
{
IpList.Clear();
GetLocalIpList(IpList, addrType);
return IpList.Count == 0 ? string.Empty : IpList[0];
}
}
// ===========================================
// Internal and debug log related stuff
// ===========================================
internal static void PrintInterfaceInfos()
{
NetDebug.WriteForce(NetLogLevel.Info, "IPv6Support: {0}", NetManager.IPv6Support);
try
{
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
{
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork ||
ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
{
NetDebug.WriteForce(
NetLogLevel.Info,
"Interface: {0}, Type: {1}, Ip: {2}, OpStatus: {3}",
ni.Name,
ni.NetworkInterfaceType.ToString(),
ip.Address.ToString(),
ni.OperationalStatus.ToString());
}
}
}
}
catch (Exception e)
{
NetDebug.WriteForce(NetLogLevel.Info, "Error while getting interface infos: {0}", e.ToString());
}
}
internal static int RelativeSequenceNumber(int number, int expected)
{
return (number - expected + NetConstants.MaxSequence + NetConstants.HalfMaxSequence) % NetConstants.MaxSequence - NetConstants.HalfMaxSequence;
}
internal static T[] AllocatePinnedUninitializedArray<T>(int count) where T : unmanaged
{
#if NET5_0_OR_GREATER || NET5_0
return GC.AllocateUninitializedArray<T>(count, true);
#else
return new T[count];
#endif
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab6438405a73f8c46ac22cd2f259a0e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,75 @@

using System.Net;
namespace LiteNetLib
{
public class PausedSocketFix
{
public bool ApplicationFocused { get; private set; }
private NetManager _netManager;
private IPAddress _ipv4;
private IPAddress _ipv6;
private int _port;
private bool _manualMode;
public PausedSocketFix()
{
UnityEngine.Application.focusChanged += Application_focusChanged;
}
public PausedSocketFix(NetManager netManager, IPAddress ipv4, IPAddress ipv6, int port, bool manualMode) : this()
{
Initialize(netManager, ipv4, ipv6, port, manualMode);
}
~PausedSocketFix()
{
UnityEngine.Application.focusChanged -= Application_focusChanged;
}
public void Initialize(NetManager netManager, IPAddress ipv4, IPAddress ipv6, int port, bool manualMode)
{
_netManager = netManager;
_ipv4 = ipv4;
_ipv6 = ipv6;
_port = port;
_manualMode = manualMode;
}
private void Application_focusChanged(bool focused)
{
ApplicationFocused = focused;
//If coming back into focus see if a reconnect is needed.
if (focused)
TryReconnect();
}
private void TryReconnect()
{
if (_netManager == null)
return;
//Was intentionally disconnected at some point.
if (!_netManager.IsRunning)
return;
if (!_netManager.IsClient)
return;
//Socket is still running.
if (_netManager.SocketActive(false) || _netManager.SocketActive(true))
return;
//Socket isn't running but should be. Try to start again.
if (!_netManager.Start(_ipv4, _ipv6, _port, _manualMode))
{
NetDebug.WriteError($"[S] Cannot restore connection. Ipv4 {_ipv4}, Ipv6 {_ipv6}, Port {_port}, ManualMode {_manualMode}");
_netManager.CloseSocket(false);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d69976ae1e5f344daec35f343bbe189
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
namespace LiteNetLib
{
public readonly ref struct PooledPacket
{
internal readonly NetPacket _packet;
internal readonly byte _channelNumber;
/// <summary>
/// Maximum data size that you can put into such packet
/// </summary>
public readonly int MaxUserDataSize;
/// <summary>
/// Offset for user data when writing to Data array
/// </summary>
public readonly int UserDataOffset;
/// <summary>
/// Raw packet data. Do not modify header! Use UserDataOffset as start point for your data
/// </summary>
public byte[] Data => _packet.RawData;
internal PooledPacket(NetPacket packet, int maxDataSize, byte channelNumber)
{
_packet = packet;
UserDataOffset = _packet.GetHeaderSize();
_packet.Size = UserDataOffset;
MaxUserDataSize = maxDataSize - UserDataOffset;
_channelNumber = channelNumber;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76e47a542bb12e043b874c7180d98319
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,336 @@
using System;
namespace LiteNetLib
{
internal sealed class ReliableChannel : BaseChannel
{
private struct PendingPacket
{
private NetPacket _packet;
private long _timeStamp;
private bool _isSent;
public override string ToString()
{
return _packet == null ? "Empty" : _packet.Sequence.ToString();
}
public void Init(NetPacket packet)
{
_packet = packet;
_isSent = false;
}
//Returns true if there is a pending packet inside
public bool TrySend(long currentTime, NetPeer peer)
{
if (_packet == null)
return false;
if (_isSent) //check send time
{
double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond;
double packetHoldTime = currentTime - _timeStamp;
if (packetHoldTime < resendDelay)
return true;
NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay);
}
_timeStamp = currentTime;
_isSent = true;
peer.SendUserData(_packet);
return true;
}
public bool Clear(NetPeer peer)
{
if (_packet != null)
{
peer.RecycleAndDeliver(_packet);
_packet = null;
return true;
}
return false;
}
}
private readonly NetPacket _outgoingAcks; //for send acks
private readonly PendingPacket[] _pendingPackets; //for unacked packets and duplicates
private readonly NetPacket[] _receivedPackets; //for order
private readonly bool[] _earlyReceived; //for unordered
private int _localSeqence;
private int _remoteSequence;
private int _localWindowStart;
private int _remoteWindowStart;
private bool _mustSendAcks;
private readonly DeliveryMethod _deliveryMethod;
private readonly bool _ordered;
private readonly int _windowSize;
private const int BitsInByte = 8;
private readonly byte _id;
public ReliableChannel(NetPeer peer, bool ordered, byte id) : base(peer)
{
_id = id;
_windowSize = NetConstants.DefaultWindowSize;
_ordered = ordered;
_pendingPackets = new PendingPacket[_windowSize];
for (int i = 0; i < _pendingPackets.Length; i++)
_pendingPackets[i] = new PendingPacket();
if (_ordered)
{
_deliveryMethod = DeliveryMethod.ReliableOrdered;
_receivedPackets = new NetPacket[_windowSize];
}
else
{
_deliveryMethod = DeliveryMethod.ReliableUnordered;
_earlyReceived = new bool[_windowSize];
}
_localWindowStart = 0;
_localSeqence = 0;
_remoteSequence = 0;
_remoteWindowStart = 0;
_outgoingAcks = new NetPacket(PacketProperty.Ack, (_windowSize - 1) / BitsInByte + 2) {ChannelId = id};
}
//ProcessAck in packet
private void ProcessAck(NetPacket packet)
{
if (packet.Size != _outgoingAcks.Size)
{
NetDebug.Write("[PA]Invalid acks packet size");
return;
}
ushort ackWindowStart = packet.Sequence;
int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart);
if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0)
{
NetDebug.Write("[PA]Bad window start");
return;
}
//check relevance
if (windowRel >= _windowSize)
{
NetDebug.Write("[PA]Old acks");
return;
}
byte[] acksData = packet.RawData;
lock (_pendingPackets)
{
for (int pendingSeq = _localWindowStart;
pendingSeq != _localSeqence;
pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence)
{
int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart);
if (rel >= _windowSize)
{
NetDebug.Write("[PA]REL: " + rel);
break;
}
int pendingIdx = pendingSeq % _windowSize;
int currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte;
int currentBit = pendingIdx % BitsInByte;
if ((acksData[currentByte] & (1 << currentBit)) == 0)
{
if (Peer.NetManager.EnableStatistics)
{
Peer.Statistics.IncrementPacketLoss();
Peer.NetManager.Statistics.IncrementPacketLoss();
}
//Skip false ack
NetDebug.Write("[PA]False ack: {0}", pendingSeq);
continue;
}
if (pendingSeq == _localWindowStart)
{
//Move window
_localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence;
}
//clear packet
if (_pendingPackets[pendingIdx].Clear(Peer))
NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq);
}
}
}
protected override bool SendNextPackets()
{
if (_mustSendAcks)
{
_mustSendAcks = false;
NetDebug.Write("[RR]SendAcks");
lock(_outgoingAcks)
Peer.SendUserData(_outgoingAcks);
}
long currentTime = DateTime.UtcNow.Ticks;
bool hasPendingPackets = false;
lock (_pendingPackets)
{
//get packets from queue
while (!OutgoingQueue.IsEmpty)
{
int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart);
if (relate >= _windowSize)
break;
if (!OutgoingQueue.TryDequeue(out var netPacket))
break;
netPacket.Sequence = (ushort) _localSeqence;
netPacket.ChannelId = _id;
_pendingPackets[_localSeqence % _windowSize].Init(netPacket);
_localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence;
}
//send
for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence)
{
// Please note: TrySend is invoked on a mutable struct, it's important to not extract it into a variable here
if (_pendingPackets[pendingSeq % _windowSize].TrySend(currentTime, Peer))
hasPendingPackets = true;
}
}
return hasPendingPackets || _mustSendAcks || !OutgoingQueue.IsEmpty;
}
//Process incoming packet
public override bool ProcessPacket(NetPacket packet)
{
if (packet.Property == PacketProperty.Ack)
{
ProcessAck(packet);
return false;
}
int seq = packet.Sequence;
if (seq >= NetConstants.MaxSequence)
{
NetDebug.Write("[RR]Bad sequence");
return false;
}
int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart);
int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence);
if (relateSeq > _windowSize)
{
NetDebug.Write("[RR]Bad sequence");
return false;
}
//Drop bad packets
if (relate < 0)
{
//Too old packet doesn't ack
NetDebug.Write("[RR]ReliableInOrder too old");
return false;
}
if (relate >= _windowSize * 2)
{
//Some very new packet
NetDebug.Write("[RR]ReliableInOrder too new");
return false;
}
//If very new - move window
int ackIdx;
int ackByte;
int ackBit;
lock (_outgoingAcks)
{
if (relate >= _windowSize)
{
//New window position
int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence;
_outgoingAcks.Sequence = (ushort) newWindowStart;
//Clean old data
while (_remoteWindowStart != newWindowStart)
{
ackIdx = _remoteWindowStart % _windowSize;
ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte;
ackBit = ackIdx % BitsInByte;
_outgoingAcks.RawData[ackByte] &= (byte) ~(1 << ackBit);
_remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence;
}
}
//Final stage - process valid packet
//trigger acks send
_mustSendAcks = true;
ackIdx = seq % _windowSize;
ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte;
ackBit = ackIdx % BitsInByte;
if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0)
{
NetDebug.Write("[RR]ReliableInOrder duplicate");
//because _mustSendAcks == true
AddToPeerChannelSendQueue();
return false;
}
//save ack
_outgoingAcks.RawData[ackByte] |= (byte) (1 << ackBit);
}
AddToPeerChannelSendQueue();
//detailed check
if (seq == _remoteSequence)
{
NetDebug.Write("[RR]ReliableInOrder packet succes");
Peer.AddReliablePacket(_deliveryMethod, packet);
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
if (_ordered)
{
NetPacket p;
while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null)
{
//process holden packet
_receivedPackets[_remoteSequence % _windowSize] = null;
Peer.AddReliablePacket(_deliveryMethod, p);
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
}
}
else
{
while (_earlyReceived[_remoteSequence % _windowSize])
{
//process early packet
_earlyReceived[_remoteSequence % _windowSize] = false;
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
}
}
return true;
}
//holden packet
if (_ordered)
{
_receivedPackets[ackIdx] = packet;
}
else
{
_earlyReceived[ackIdx] = true;
Peer.AddReliablePacket(_deliveryMethod, packet);
}
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7e20c169333af54fa233b0d70b19a61
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,110 @@
using System;
namespace LiteNetLib
{
internal sealed class SequencedChannel : BaseChannel
{
private int _localSequence;
private ushort _remoteSequence;
private readonly bool _reliable;
private NetPacket _lastPacket;
private readonly NetPacket _ackPacket;
private bool _mustSendAck;
private readonly byte _id;
private long _lastPacketSendTime;
public SequencedChannel(NetPeer peer, bool reliable, byte id) : base(peer)
{
_id = id;
_reliable = reliable;
if (_reliable)
_ackPacket = new NetPacket(PacketProperty.Ack, 0) {ChannelId = id};
}
protected override bool SendNextPackets()
{
if (_reliable && OutgoingQueue.Count == 0)
{
long currentTime = DateTime.UtcNow.Ticks;
long packetHoldTime = currentTime - _lastPacketSendTime;
if (packetHoldTime >= Peer.ResendDelay * TimeSpan.TicksPerMillisecond)
{
var packet = _lastPacket;
if (packet != null)
{
_lastPacketSendTime = currentTime;
Peer.SendUserData(packet);
}
}
}
else
{
while (OutgoingQueue.TryDequeue(out var packet))
{
_localSequence = (_localSequence + 1) % NetConstants.MaxSequence;
packet.Sequence = (ushort)_localSequence;
packet.ChannelId = _id;
Peer.SendUserData(packet);
if (_reliable && OutgoingQueue.Count == 0)
{
_lastPacketSendTime = DateTime.UtcNow.Ticks;
_lastPacket = packet;
}
else
{
Peer.NetManager.PoolRecycle(packet);
}
}
}
if (_reliable && _mustSendAck)
{
_mustSendAck = false;
_ackPacket.Sequence = _remoteSequence;
Peer.SendUserData(_ackPacket);
}
return _lastPacket != null;
}
public override bool ProcessPacket(NetPacket packet)
{
if (packet.IsFragmented)
return false;
if (packet.Property == PacketProperty.Ack)
{
if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Sequence)
_lastPacket = null;
return false;
}
int relative = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence);
bool packetProcessed = false;
if (packet.Sequence < NetConstants.MaxSequence && relative > 0)
{
if (Peer.NetManager.EnableStatistics)
{
Peer.Statistics.AddPacketLoss(relative - 1);
Peer.NetManager.Statistics.AddPacketLoss(relative - 1);
}
_remoteSequence = packet.Sequence;
Peer.NetManager.CreateReceiveEvent(
packet,
_reliable ? DeliveryMethod.ReliableSequenced : DeliveryMethod.Sequenced,
(byte)(packet.ChannelId / NetConstants.ChannelTypeCount),
NetConstants.ChanneledHeaderSize,
Peer);
packetProcessed = true;
}
if (_reliable)
{
_mustSendAck = true;
AddToPeerChannelSendQueue();
}
return packetProcessed;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4e1f97908b70e642baea0fc22724282
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0c6e0e6809efaf74c8c6864f8129b89f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,150 @@
#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
#endif
#if NET5_0_OR_GREATER || NET5_0
using System.Runtime.Intrinsics.Arm;
#endif
namespace LiteNetLib.Utils
{
//Implementation from Crc32.NET
public static class CRC32C
{
public const int ChecksumSize = 4;
private const uint Poly = 0x82F63B78u;
private static readonly uint[] Table;
static CRC32C()
{
#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0
if (Sse42.IsSupported)
return;
#endif
#if NET5_0_OR_GREATER || NET5_0
if (Crc32.IsSupported)
return;
#endif
Table = NetUtils.AllocatePinnedUninitializedArray<uint>(16 * 256);
for (uint i = 0; i < 256; i++)
{
uint res = i;
for (int t = 0; t < 16; t++)
{
for (int k = 0; k < 8; k++)
res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1);
Table[t * 256 + i] = res;
}
}
}
/// <summary>
/// Compute CRC32C for data
/// </summary>
/// <param name="input">input data</param>
/// <param name="offset">offset</param>
/// <param name="length">length</param>
/// <returns>CRC32C checksum</returns>
public static uint Compute(byte[] input, int offset, int length)
{
uint crcLocal = uint.MaxValue;
#if NETCOREAPP3_0_OR_GREATER || NETCOREAPP3_1 || NET5_0
if (Sse42.IsSupported)
{
var data = new ReadOnlySpan<byte>(input, offset, length);
int processed = 0;
if (Sse42.X64.IsSupported && data.Length > sizeof(ulong))
{
processed = data.Length / sizeof(ulong) * sizeof(ulong);
var ulongs = MemoryMarshal.Cast<byte, ulong>(data.Slice(0, processed));
ulong crclong = crcLocal;
for (int i = 0; i < ulongs.Length; i++)
{
crclong = Sse42.X64.Crc32(crclong, ulongs[i]);
}
crcLocal = (uint)crclong;
}
else if (data.Length > sizeof(uint))
{
processed = data.Length / sizeof(uint) * sizeof(uint);
var uints = MemoryMarshal.Cast<byte, uint>(data.Slice(0, processed));
for (int i = 0; i < uints.Length; i++)
{
crcLocal = Sse42.Crc32(crcLocal, uints[i]);
}
}
for (int i = processed; i < data.Length; i++)
{
crcLocal = Sse42.Crc32(crcLocal, data[i]);
}
return crcLocal ^ uint.MaxValue;
}
#endif
#if NET5_0_OR_GREATER || NET5_0
if (Crc32.IsSupported)
{
var data = new ReadOnlySpan<byte>(input, offset, length);
int processed = 0;
if (Crc32.Arm64.IsSupported && data.Length > sizeof(ulong))
{
processed = data.Length / sizeof(ulong) * sizeof(ulong);
var ulongs = MemoryMarshal.Cast<byte, ulong>(data.Slice(0, processed));
for (int i = 0; i < ulongs.Length; i++)
{
crcLocal = Crc32.Arm64.ComputeCrc32C(crcLocal, ulongs[i]);
}
}
else if (data.Length > sizeof(uint))
{
processed = data.Length / sizeof(uint) * sizeof(uint);
var uints = MemoryMarshal.Cast<byte, uint>(data.Slice(0, processed));
for (int i = 0; i < uints.Length; i++)
{
crcLocal = Crc32.ComputeCrc32C(crcLocal, uints[i]);
}
}
for (int i = processed; i < data.Length; i++)
{
crcLocal = Crc32.ComputeCrc32C(crcLocal, data[i]);
}
return crcLocal ^ uint.MaxValue;
}
#endif
while (length >= 16)
{
var a = Table[(3 * 256) + input[offset + 12]]
^ Table[(2 * 256) + input[offset + 13]]
^ Table[(1 * 256) + input[offset + 14]]
^ Table[(0 * 256) + input[offset + 15]];
var b = Table[(7 * 256) + input[offset + 8]]
^ Table[(6 * 256) + input[offset + 9]]
^ Table[(5 * 256) + input[offset + 10]]
^ Table[(4 * 256) + input[offset + 11]];
var c = Table[(11 * 256) + input[offset + 4]]
^ Table[(10 * 256) + input[offset + 5]]
^ Table[(9 * 256) + input[offset + 6]]
^ Table[(8 * 256) + input[offset + 7]];
var d = Table[(15 * 256) + ((byte)crcLocal ^ input[offset])]
^ Table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])]
^ Table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])]
^ Table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])];
crcLocal = d ^ c ^ b ^ a;
offset += 16;
length -= 16;
}
while (--length >= 0)
crcLocal = Table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8;
return crcLocal ^ uint.MaxValue;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 912ba506b0945b743be5c4129177024c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,175 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LiteNetLib.Utils
{
public static class FastBitConverter
{
#if (LITENETLIB_UNSAFE || LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER) && !BIGENDIAN
#if LITENETLIB_UNSAFE
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void GetBytes<T>(byte[] bytes, int startIndex, T value) where T : unmanaged
{
int size = sizeof(T);
if (bytes.Length < startIndex + size)
ThrowIndexOutOfRangeException();
#if LITENETLIB_UNSAFELIB || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER
Unsafe.As<byte, T>(ref bytes[startIndex]) = value;
#else
fixed (byte* ptr = &bytes[startIndex])
{
#if UNITY_ANDROID
// On some android systems, assigning *(T*)ptr throws a NRE if
// the ptr isn't aligned (i.e. if Position is 1,2,3,5, etc.).
// Here we have to use memcpy.
//
// => we can't get a pointer of a struct in C# without
// marshalling allocations
// => instead, we stack allocate an array of type T and use that
// => stackalloc avoids GC and is very fast. it only works for
// value types, but all blittable types are anyway.
T* valueBuffer = stackalloc T[1] { value };
UnsafeUtility.MemCpy(ptr, valueBuffer, size);
#else
*(T*)ptr = value;
#endif
}
#endif
}
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes<T>(byte[] bytes, int startIndex, T value) where T : unmanaged
{
if (bytes.Length < startIndex + Unsafe.SizeOf<T>())
ThrowIndexOutOfRangeException();
Unsafe.As<byte, T>(ref bytes[startIndex]) = value;
}
#endif
private static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
#else
[StructLayout(LayoutKind.Explicit)]
private struct ConverterHelperDouble
{
[FieldOffset(0)]
public ulong Along;
[FieldOffset(0)]
public double Adouble;
}
[StructLayout(LayoutKind.Explicit)]
private struct ConverterHelperFloat
{
[FieldOffset(0)]
public int Aint;
[FieldOffset(0)]
public float Afloat;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WriteLittleEndian(byte[] buffer, int offset, ulong data)
{
#if BIGENDIAN
buffer[offset + 7] = (byte)(data);
buffer[offset + 6] = (byte)(data >> 8);
buffer[offset + 5] = (byte)(data >> 16);
buffer[offset + 4] = (byte)(data >> 24);
buffer[offset + 3] = (byte)(data >> 32);
buffer[offset + 2] = (byte)(data >> 40);
buffer[offset + 1] = (byte)(data >> 48);
buffer[offset ] = (byte)(data >> 56);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
buffer[offset + 2] = (byte)(data >> 16);
buffer[offset + 3] = (byte)(data >> 24);
buffer[offset + 4] = (byte)(data >> 32);
buffer[offset + 5] = (byte)(data >> 40);
buffer[offset + 6] = (byte)(data >> 48);
buffer[offset + 7] = (byte)(data >> 56);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WriteLittleEndian(byte[] buffer, int offset, int data)
{
#if BIGENDIAN
buffer[offset + 3] = (byte)(data);
buffer[offset + 2] = (byte)(data >> 8);
buffer[offset + 1] = (byte)(data >> 16);
buffer[offset ] = (byte)(data >> 24);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
buffer[offset + 2] = (byte)(data >> 16);
buffer[offset + 3] = (byte)(data >> 24);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteLittleEndian(byte[] buffer, int offset, short data)
{
#if BIGENDIAN
buffer[offset + 1] = (byte)(data);
buffer[offset ] = (byte)(data >> 8);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, double value)
{
ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value };
WriteLittleEndian(bytes, startIndex, ch.Along);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, float value)
{
ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value };
WriteLittleEndian(bytes, startIndex, ch.Aint);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, short value)
{
WriteLittleEndian(bytes, startIndex, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, ushort value)
{
WriteLittleEndian(bytes, startIndex, (short)value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, int value)
{
WriteLittleEndian(bytes, startIndex, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, uint value)
{
WriteLittleEndian(bytes, startIndex, (int)value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, long value)
{
WriteLittleEndian(bytes, startIndex, (ulong)value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(byte[] bytes, int startIndex, ulong value)
{
WriteLittleEndian(bytes, startIndex, value);
}
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 21018f84ccfb8244e99b5ec22ceb91c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
namespace LiteNetLib.Utils
{
public interface INetSerializable
{
void Serialize(NetDataWriter writer);
void Deserialize(NetDataReader reader);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c26b17a74d3fb8b498fc89d253f1742a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,680 @@
using System;
using System.Net;
using System.Text;
namespace LiteNetLib.Utils
{
public class NetDataReader
{
protected byte[] _data;
protected int _position;
protected int _dataSize;
private int _offset;
public byte[] RawData => _data;
public int RawDataSize => _dataSize;
public int UserDataOffset => _offset;
public int UserDataSize => _dataSize - _offset;
public bool IsNull => _data == null;
public int Position => _position;
public bool EndOfData => _position == _dataSize;
public int AvailableBytes => _dataSize - _position;
// Cache encoding instead of creating it with BinaryWriter each time
// 1000 readers before: 1MB GC, 30ms
// 1000 readers after: .8MB GC, 18ms
private static readonly UTF8Encoding _uTF8Encoding = new UTF8Encoding(false, true);
public void SkipBytes(int count)
{
_position += count;
}
public void SetPosition(int position)
{
_position = position;
}
public void SetSource(NetDataWriter dataWriter)
{
_data = dataWriter.Data;
_position = 0;
_offset = 0;
_dataSize = dataWriter.Length;
}
public void SetSource(byte[] source)
{
_data = source;
_position = 0;
_offset = 0;
_dataSize = source.Length;
}
public void SetSource(byte[] source, int offset, int maxSize)
{
_data = source;
_position = offset;
_offset = offset;
_dataSize = maxSize;
}
public NetDataReader()
{
}
public NetDataReader(NetDataWriter writer)
{
SetSource(writer);
}
public NetDataReader(byte[] source)
{
SetSource(source);
}
public NetDataReader(byte[] source, int offset, int maxSize)
{
SetSource(source, offset, maxSize);
}
#region GetMethods
public IPEndPoint GetNetEndPoint()
{
string host = GetString(1000);
int port = GetInt();
return NetUtils.MakeEndPoint(host, port);
}
public byte GetByte()
{
byte res = _data[_position];
_position += 1;
return res;
}
public sbyte GetSByte()
{
var b = (sbyte)_data[_position];
_position++;
return b;
}
public bool[] GetBoolArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new bool[size];
Buffer.BlockCopy(_data, _position, arr, 0, size);
_position += size;
return arr;
}
public ushort[] GetUShortArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new ushort[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 2);
_position += size * 2;
return arr;
}
public short[] GetShortArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new short[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 2);
_position += size * 2;
return arr;
}
public long[] GetLongArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new long[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 8);
_position += size * 8;
return arr;
}
public ulong[] GetULongArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new ulong[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 8);
_position += size * 8;
return arr;
}
public int[] GetIntArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new int[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 4);
_position += size * 4;
return arr;
}
public uint[] GetUIntArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new uint[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 4);
_position += size * 4;
return arr;
}
public float[] GetFloatArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new float[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 4);
_position += size * 4;
return arr;
}
public double[] GetDoubleArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new double[size];
Buffer.BlockCopy(_data, _position, arr, 0, size * 8);
_position += size * 8;
return arr;
}
public string[] GetStringArray()
{
ushort arraySize = GetUShort();
var arr = new string[arraySize];
for (int i = 0; i < arraySize; i++)
{
arr[i] = GetString();
}
return arr;
}
public string[] GetStringArray(int maxStringLength)
{
ushort arraySize = GetUShort();
var arr = new string[arraySize];
for (int i = 0; i < arraySize; i++)
{
arr[i] = GetString(maxStringLength);
}
return arr;
}
public bool GetBool()
{
bool res = _data[_position] > 0;
_position += 1;
return res;
}
public char GetChar()
{
return (char)GetUShort();
}
public ushort GetUShort()
{
ushort result = BitConverter.ToUInt16(_data, _position);
_position += 2;
return result;
}
public short GetShort()
{
short result = BitConverter.ToInt16(_data, _position);
_position += 2;
return result;
}
public long GetLong()
{
long result = BitConverter.ToInt64(_data, _position);
_position += 8;
return result;
}
public ulong GetULong()
{
ulong result = BitConverter.ToUInt64(_data, _position);
_position += 8;
return result;
}
public int GetInt()
{
int result = BitConverter.ToInt32(_data, _position);
_position += 4;
return result;
}
public uint GetUInt()
{
uint result = BitConverter.ToUInt32(_data, _position);
_position += 4;
return result;
}
public float GetFloat()
{
float result = BitConverter.ToSingle(_data, _position);
_position += 4;
return result;
}
public double GetDouble()
{
double result = BitConverter.ToDouble(_data, _position);
_position += 8;
return result;
}
/// <summary>
/// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
/// </summary>
/// <returns>"string.Empty" if value > "maxLength"</returns>
public string GetString(int maxLength)
{
ushort size = GetUShort();
if (size == 0)
{
return null;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
ArraySegment<byte> data = GetBytesSegment(actualSize);
return (maxLength > 0 && _uTF8Encoding.GetCharCount(data.Array, data.Offset, data.Count) > maxLength) ?
string.Empty :
_uTF8Encoding.GetString(data.Array, data.Offset, data.Count);
}
public string GetString()
{
ushort size = GetUShort();
if (size == 0)
{
return null;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
ArraySegment<byte> data = GetBytesSegment(actualSize);
return _uTF8Encoding.GetString(data.Array, data.Offset, data.Count);
}
public ArraySegment<byte> GetBytesSegment(int count)
{
ArraySegment<byte> segment = new ArraySegment<byte>(_data, _position, count);
_position += count;
return segment;
}
public ArraySegment<byte> GetRemainingBytesSegment()
{
ArraySegment<byte> segment = new ArraySegment<byte>(_data, _position, AvailableBytes);
_position = _data.Length;
return segment;
}
public T Get<T>() where T : INetSerializable, new()
{
var obj = new T();
obj.Deserialize(this);
return obj;
}
public byte[] GetRemainingBytes()
{
byte[] outgoingData = new byte[AvailableBytes];
Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes);
_position = _data.Length;
return outgoingData;
}
public void GetBytes(byte[] destination, int start, int count)
{
Buffer.BlockCopy(_data, _position, destination, start, count);
_position += count;
}
public void GetBytes(byte[] destination, int count)
{
Buffer.BlockCopy(_data, _position, destination, 0, count);
_position += count;
}
public sbyte[] GetSBytesWithLength()
{
int length = GetInt();
sbyte[] outgoingData = new sbyte[length];
Buffer.BlockCopy(_data, _position, outgoingData, 0, length);
_position += length;
return outgoingData;
}
public byte[] GetBytesWithLength()
{
int length = GetInt();
byte[] outgoingData = new byte[length];
Buffer.BlockCopy(_data, _position, outgoingData, 0, length);
_position += length;
return outgoingData;
}
#endregion
#region PeekMethods
public byte PeekByte()
{
return _data[_position];
}
public sbyte PeekSByte()
{
return (sbyte)_data[_position];
}
public bool PeekBool()
{
return _data[_position] > 0;
}
public char PeekChar()
{
return (char)PeekUShort();
}
public ushort PeekUShort()
{
return BitConverter.ToUInt16(_data, _position);
}
public short PeekShort()
{
return BitConverter.ToInt16(_data, _position);
}
public long PeekLong()
{
return BitConverter.ToInt64(_data, _position);
}
public ulong PeekULong()
{
return BitConverter.ToUInt64(_data, _position);
}
public int PeekInt()
{
return BitConverter.ToInt32(_data, _position);
}
public uint PeekUInt()
{
return BitConverter.ToUInt32(_data, _position);
}
public float PeekFloat()
{
return BitConverter.ToSingle(_data, _position);
}
public double PeekDouble()
{
return BitConverter.ToDouble(_data, _position);
}
public string PeekString(int maxLength)
{
ushort size = PeekUShort();
if (size == 0)
{
return null;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
return (maxLength > 0 && _uTF8Encoding.GetCharCount(_data, _position + 2, actualSize) > maxLength) ?
string.Empty :
_uTF8Encoding.GetString(_data, _position + 2, actualSize);
}
public string PeekString()
{
ushort size = PeekUShort();
if (size == 0)
{
return null;
}
int actualSize = size - 1;
if (actualSize >= NetDataWriter.StringBufferMaxLength)
{
return null;
}
return _uTF8Encoding.GetString(_data, _position + 2, actualSize);
}
#endregion
#region TryGetMethods
public bool TryGetByte(out byte result)
{
if (AvailableBytes >= 1)
{
result = GetByte();
return true;
}
result = 0;
return false;
}
public bool TryGetSByte(out sbyte result)
{
if (AvailableBytes >= 1)
{
result = GetSByte();
return true;
}
result = 0;
return false;
}
public bool TryGetBool(out bool result)
{
if (AvailableBytes >= 1)
{
result = GetBool();
return true;
}
result = false;
return false;
}
public bool TryGetChar(out char result)
{
if (!TryGetUShort(out ushort uShortValue))
{
result = '\0';
return false;
}
result = (char)uShortValue;
return true;
}
public bool TryGetShort(out short result)
{
if (AvailableBytes >= 2)
{
result = GetShort();
return true;
}
result = 0;
return false;
}
public bool TryGetUShort(out ushort result)
{
if (AvailableBytes >= 2)
{
result = GetUShort();
return true;
}
result = 0;
return false;
}
public bool TryGetInt(out int result)
{
if (AvailableBytes >= 4)
{
result = GetInt();
return true;
}
result = 0;
return false;
}
public bool TryGetUInt(out uint result)
{
if (AvailableBytes >= 4)
{
result = GetUInt();
return true;
}
result = 0;
return false;
}
public bool TryGetLong(out long result)
{
if (AvailableBytes >= 8)
{
result = GetLong();
return true;
}
result = 0;
return false;
}
public bool TryGetULong(out ulong result)
{
if (AvailableBytes >= 8)
{
result = GetULong();
return true;
}
result = 0;
return false;
}
public bool TryGetFloat(out float result)
{
if (AvailableBytes >= 4)
{
result = GetFloat();
return true;
}
result = 0;
return false;
}
public bool TryGetDouble(out double result)
{
if (AvailableBytes >= 8)
{
result = GetDouble();
return true;
}
result = 0;
return false;
}
public bool TryGetString(out string result)
{
if (AvailableBytes >= 2)
{
ushort strSize = PeekUShort();
if (AvailableBytes >= strSize + 1)
{
result = GetString();
return true;
}
}
result = null;
return false;
}
public bool TryGetStringArray(out string[] result)
{
ushort strArrayLength;
if (!TryGetUShort(out strArrayLength))
{
result = null;
return false;
}
result = new string[strArrayLength];
for (int i = 0; i < strArrayLength; i++)
{
if (!TryGetString(out result[i]))
{
result = null;
return false;
}
}
return true;
}
public bool TryGetBytesWithLength(out byte[] result)
{
if (AvailableBytes >= 4)
{
var length = PeekInt();
if (length >= 0 && AvailableBytes >= length + 4)
{
result = GetBytesWithLength();
return true;
}
}
result = null;
return false;
}
#endregion
public void Clear()
{
_position = 0;
_dataSize = 0;
_data = null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2876e12b475627f448ca5a6850748bc3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,379 @@
using System;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
namespace LiteNetLib.Utils
{
public class NetDataWriter
{
protected byte[] _data;
protected int _position;
private const int InitialSize = 64;
private readonly bool _autoResize;
public int Capacity => _data.Length;
public byte[] Data => _data;
public int Length => _position;
// Cache encoding instead of creating it with BinaryWriter each time
// 1000 readers before: 1MB GC, 30ms
// 1000 readers after: .8MB GC, 18ms
private static readonly UTF8Encoding _uTF8Encoding = new UTF8Encoding(false, true);
public const int StringBufferMaxLength = 1024 * 32; // <- short.MaxValue + 1
private readonly byte[] _stringBuffer = new byte[StringBufferMaxLength];
public NetDataWriter() : this(true, InitialSize)
{
}
public NetDataWriter(bool autoResize) : this(autoResize, InitialSize)
{
}
public NetDataWriter(bool autoResize, int initialSize)
{
_data = new byte[initialSize];
_autoResize = autoResize;
}
/// <summary>
/// Creates NetDataWriter from existing ByteArray
/// </summary>
/// <param name="bytes">Source byte array</param>
/// <param name="copy">Copy array to new location or use existing</param>
public static NetDataWriter FromBytes(byte[] bytes, bool copy)
{
if (copy)
{
var netDataWriter = new NetDataWriter(true, bytes.Length);
netDataWriter.Put(bytes);
return netDataWriter;
}
return new NetDataWriter(true, 0) {_data = bytes, _position = bytes.Length};
}
/// <summary>
/// Creates NetDataWriter from existing ByteArray (always copied data)
/// </summary>
/// <param name="bytes">Source byte array</param>
/// <param name="offset">Offset of array</param>
/// <param name="length">Length of array</param>
public static NetDataWriter FromBytes(byte[] bytes, int offset, int length)
{
var netDataWriter = new NetDataWriter(true, bytes.Length);
netDataWriter.Put(bytes, offset, length);
return netDataWriter;
}
public static NetDataWriter FromString(string value)
{
var netDataWriter = new NetDataWriter();
netDataWriter.Put(value);
return netDataWriter;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResizeIfNeed(int newSize)
{
if (_data.Length < newSize)
{
Array.Resize(ref _data, Math.Max(newSize, _data.Length * 2));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureFit(int additionalSize)
{
if (_data.Length < _position + additionalSize)
{
Array.Resize(ref _data, Math.Max(_position + additionalSize, _data.Length * 2));
}
}
public void Reset(int size)
{
ResizeIfNeed(size);
_position = 0;
}
public void Reset()
{
_position = 0;
}
public byte[] CopyData()
{
byte[] resultData = new byte[_position];
Buffer.BlockCopy(_data, 0, resultData, 0, _position);
return resultData;
}
/// <summary>
/// Sets position of NetDataWriter to rewrite previous values
/// </summary>
/// <param name="position">new byte position</param>
/// <returns>previous position of data writer</returns>
public int SetPosition(int position)
{
int prevPosition = _position;
_position = position;
return prevPosition;
}
public void Put(float value)
{
if (_autoResize)
ResizeIfNeed(_position + 4);
FastBitConverter.GetBytes(_data, _position, value);
_position += 4;
}
public void Put(double value)
{
if (_autoResize)
ResizeIfNeed(_position + 8);
FastBitConverter.GetBytes(_data, _position, value);
_position += 8;
}
public void Put(long value)
{
if (_autoResize)
ResizeIfNeed(_position + 8);
FastBitConverter.GetBytes(_data, _position, value);
_position += 8;
}
public void Put(ulong value)
{
if (_autoResize)
ResizeIfNeed(_position + 8);
FastBitConverter.GetBytes(_data, _position, value);
_position += 8;
}
public void Put(int value)
{
if (_autoResize)
ResizeIfNeed(_position + 4);
FastBitConverter.GetBytes(_data, _position, value);
_position += 4;
}
public void Put(uint value)
{
if (_autoResize)
ResizeIfNeed(_position + 4);
FastBitConverter.GetBytes(_data, _position, value);
_position += 4;
}
public void Put(char value)
{
Put((ushort)value);
}
public void Put(ushort value)
{
if (_autoResize)
ResizeIfNeed(_position + 2);
FastBitConverter.GetBytes(_data, _position, value);
_position += 2;
}
public void Put(short value)
{
if (_autoResize)
ResizeIfNeed(_position + 2);
FastBitConverter.GetBytes(_data, _position, value);
_position += 2;
}
public void Put(sbyte value)
{
if (_autoResize)
ResizeIfNeed(_position + 1);
_data[_position] = (byte)value;
_position++;
}
public void Put(byte value)
{
if (_autoResize)
ResizeIfNeed(_position + 1);
_data[_position] = value;
_position++;
}
public void Put(byte[] data, int offset, int length)
{
if (_autoResize)
ResizeIfNeed(_position + length);
Buffer.BlockCopy(data, offset, _data, _position, length);
_position += length;
}
public void Put(byte[] data)
{
if (_autoResize)
ResizeIfNeed(_position + data.Length);
Buffer.BlockCopy(data, 0, _data, _position, data.Length);
_position += data.Length;
}
public void PutSBytesWithLength(sbyte[] data, int offset, int length)
{
if (_autoResize)
ResizeIfNeed(_position + length + 4);
FastBitConverter.GetBytes(_data, _position, length);
Buffer.BlockCopy(data, offset, _data, _position + 4, length);
_position += length + 4;
}
public void PutSBytesWithLength(sbyte[] data)
{
if (_autoResize)
ResizeIfNeed(_position + data.Length + 4);
FastBitConverter.GetBytes(_data, _position, data.Length);
Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length);
_position += data.Length + 4;
}
public void PutBytesWithLength(byte[] data, int offset, int length)
{
if (_autoResize)
ResizeIfNeed(_position + length + 4);
FastBitConverter.GetBytes(_data, _position, length);
Buffer.BlockCopy(data, offset, _data, _position + 4, length);
_position += length + 4;
}
public void PutBytesWithLength(byte[] data)
{
if (_autoResize)
ResizeIfNeed(_position + data.Length + 4);
FastBitConverter.GetBytes(_data, _position, data.Length);
Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length);
_position += data.Length + 4;
}
public void Put(bool value)
{
Put((byte)(value ? 1 : 0));
}
private void PutArray(Array arr, int sz)
{
ushort length = arr == null ? (ushort) 0 : (ushort)arr.Length;
sz *= length;
if (_autoResize)
ResizeIfNeed(_position + sz + 2);
FastBitConverter.GetBytes(_data, _position, length);
if (arr != null)
Buffer.BlockCopy(arr, 0, _data, _position + 2, sz);
_position += sz + 2;
}
public void PutArray(float[] value)
{
PutArray(value, 4);
}
public void PutArray(double[] value)
{
PutArray(value, 8);
}
public void PutArray(long[] value)
{
PutArray(value, 8);
}
public void PutArray(ulong[] value)
{
PutArray(value, 8);
}
public void PutArray(int[] value)
{
PutArray(value, 4);
}
public void PutArray(uint[] value)
{
PutArray(value, 4);
}
public void PutArray(ushort[] value)
{
PutArray(value, 2);
}
public void PutArray(short[] value)
{
PutArray(value, 2);
}
public void PutArray(bool[] value)
{
PutArray(value, 1);
}
public void PutArray(string[] value)
{
ushort strArrayLength = value == null ? (ushort)0 : (ushort)value.Length;
Put(strArrayLength);
for (int i = 0; i < strArrayLength; i++)
Put(value[i]);
}
public void PutArray(string[] value, int strMaxLength)
{
ushort strArrayLength = value == null ? (ushort)0 : (ushort)value.Length;
Put(strArrayLength);
for (int i = 0; i < strArrayLength; i++)
Put(value[i], strMaxLength);
}
public void Put(IPEndPoint endPoint)
{
Put(endPoint.Address.ToString());
Put(endPoint.Port);
}
public void Put(string value)
{
Put(value, 0);
}
/// <summary>
/// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
/// </summary>
public void Put(string value, int maxLength)
{
if (value == null)
{
Put((ushort)0);
return;
}
int length = maxLength > 0 && value.Length > maxLength ? maxLength : value.Length;
int size = _uTF8Encoding.GetBytes(value, 0, length, _stringBuffer, 0);
if (size >= StringBufferMaxLength)
{
Put((ushort)0);
return;
}
Put(checked((ushort)(size + 1)));
Put(_stringBuffer, 0, size);
}
public void Put<T>(T obj) where T : INetSerializable
{
obj.Serialize(this);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb3b55fb59ddd9044b1fd56b8543053b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
namespace LiteNetLib.Utils
{
public class NetPacketProcessor
{
private static class HashCache<T>
{
public static readonly ulong Id;
//FNV-1 64 bit hash
static HashCache()
{
ulong hash = 14695981039346656037UL; //offset
string typeName = typeof(T).ToString();
for (var i = 0; i < typeName.Length; i++)
{
hash ^= typeName[i];
hash *= 1099511628211UL; //prime
}
Id = hash;
}
}
protected delegate void SubscribeDelegate(NetDataReader reader, object userData);
private readonly NetSerializer _netSerializer;
private readonly Dictionary<ulong, SubscribeDelegate> _callbacks = new Dictionary<ulong, SubscribeDelegate>();
private readonly NetDataWriter _netDataWriter = new NetDataWriter();
public NetPacketProcessor()
{
_netSerializer = new NetSerializer();
}
public NetPacketProcessor(int maxStringLength)
{
_netSerializer = new NetSerializer(maxStringLength);
}
protected virtual ulong GetHash<T>()
{
return HashCache<T>.Id;
}
protected virtual SubscribeDelegate GetCallbackFromData(NetDataReader reader)
{
ulong hash = reader.GetULong();
if (!_callbacks.TryGetValue(hash, out var action))
{
throw new ParseException("Undefined packet in NetDataReader");
}
return action;
}
protected virtual void WriteHash<T>(NetDataWriter writer)
{
writer.Put(GetHash<T>());
}
/// <summary>
/// Register nested property type
/// </summary>
/// <typeparam name="T">INetSerializable structure</typeparam>
public void RegisterNestedType<T>() where T : struct, INetSerializable
{
_netSerializer.RegisterNestedType<T>();
}
/// <summary>
/// Register nested property type
/// </summary>
/// <param name="writeDelegate"></param>
/// <param name="readDelegate"></param>
public void RegisterNestedType<T>(Action<NetDataWriter, T> writeDelegate, Func<NetDataReader, T> readDelegate)
{
_netSerializer.RegisterNestedType<T>(writeDelegate, readDelegate);
}
/// <summary>
/// Register nested property type
/// </summary>
/// <typeparam name="T">INetSerializable class</typeparam>
public void RegisterNestedType<T>(Func<T> constructor) where T : class, INetSerializable
{
_netSerializer.RegisterNestedType(constructor);
}
/// <summary>
/// Reads all available data from NetDataReader and calls OnReceive delegates
/// </summary>
/// <param name="reader">NetDataReader with packets data</param>
public void ReadAllPackets(NetDataReader reader)
{
while (reader.AvailableBytes > 0)
ReadPacket(reader);
}
/// <summary>
/// Reads all available data from NetDataReader and calls OnReceive delegates
/// </summary>
/// <param name="reader">NetDataReader with packets data</param>
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
/// <exception cref="ParseException">Malformed packet</exception>
public void ReadAllPackets(NetDataReader reader, object userData)
{
while (reader.AvailableBytes > 0)
ReadPacket(reader, userData);
}
/// <summary>
/// Reads one packet from NetDataReader and calls OnReceive delegate
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <exception cref="ParseException">Malformed packet</exception>
public void ReadPacket(NetDataReader reader)
{
ReadPacket(reader, null);
}
public void Send<T>(NetPeer peer, T packet, DeliveryMethod options) where T : class, new()
{
_netDataWriter.Reset();
Write(_netDataWriter, packet);
peer.Send(_netDataWriter, options);
}
public void SendNetSerializable<T>(NetPeer peer, ref T packet, DeliveryMethod options) where T : INetSerializable
{
_netDataWriter.Reset();
WriteNetSerializable(_netDataWriter, ref packet);
peer.Send(_netDataWriter, options);
}
public void Send<T>(NetManager manager, T packet, DeliveryMethod options) where T : class, new()
{
_netDataWriter.Reset();
Write(_netDataWriter, packet);
manager.SendToAll(_netDataWriter, options);
}
public void SendNetSerializable<T>(NetManager manager, ref T packet, DeliveryMethod options) where T : INetSerializable
{
_netDataWriter.Reset();
WriteNetSerializable(_netDataWriter, ref packet);
manager.SendToAll(_netDataWriter, options);
}
public void Write<T>(NetDataWriter writer, T packet) where T : class, new()
{
WriteHash<T>(writer);
_netSerializer.Serialize(writer, packet);
}
public void WriteNetSerializable<T>(NetDataWriter writer, ref T packet) where T : INetSerializable
{
WriteHash<T>(writer);
packet.Serialize(writer);
}
/// <summary>
/// Reads one packet from NetDataReader and calls OnReceive delegate
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
/// <exception cref="ParseException">Malformed packet</exception>
public void ReadPacket(NetDataReader reader, object userData)
{
GetCallbackFromData(reader)(reader, userData);
}
/// <summary>
/// Register and subscribe to packet receive event
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <param name="packetConstructor">Method that constructs packet instead of slow Activator.CreateInstance</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Subscribe<T>(Action<T> onReceive, Func<T> packetConstructor) where T : class, new()
{
_netSerializer.Register<T>();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var reference = packetConstructor();
_netSerializer.Deserialize(reader, reference);
onReceive(reference);
};
}
/// <summary>
/// Register and subscribe to packet receive event (with userData)
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <param name="packetConstructor">Method that constructs packet instead of slow Activator.CreateInstance</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Subscribe<T, TUserData>(Action<T, TUserData> onReceive, Func<T> packetConstructor) where T : class, new()
{
_netSerializer.Register<T>();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var reference = packetConstructor();
_netSerializer.Deserialize(reader, reference);
onReceive(reference, (TUserData)userData);
};
}
/// <summary>
/// Register and subscribe to packet receive event
/// This method will overwrite last received packet class on receive (less garbage)
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void SubscribeReusable<T>(Action<T> onReceive) where T : class, new()
{
_netSerializer.Register<T>();
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
_netSerializer.Deserialize(reader, reference);
onReceive(reference);
};
}
/// <summary>
/// Register and subscribe to packet receive event
/// This method will overwrite last received packet class on receive (less garbage)
/// </summary>
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void SubscribeReusable<T, TUserData>(Action<T, TUserData> onReceive) where T : class, new()
{
_netSerializer.Register<T>();
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
_netSerializer.Deserialize(reader, reference);
onReceive(reference, (TUserData)userData);
};
}
public void SubscribeNetSerializable<T, TUserData>(
Action<T, TUserData> onReceive,
Func<T> packetConstructor) where T : INetSerializable
{
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var pkt = packetConstructor();
pkt.Deserialize(reader);
onReceive(pkt, (TUserData)userData);
};
}
public void SubscribeNetSerializable<T>(
Action<T> onReceive,
Func<T> packetConstructor) where T : INetSerializable
{
_callbacks[GetHash<T>()] = (reader, userData) =>
{
var pkt = packetConstructor();
pkt.Deserialize(reader);
onReceive(pkt);
};
}
public void SubscribeNetSerializable<T, TUserData>(
Action<T, TUserData> onReceive) where T : INetSerializable, new()
{
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
reference.Deserialize(reader);
onReceive(reference, (TUserData)userData);
};
}
public void SubscribeNetSerializable<T>(
Action<T> onReceive) where T : INetSerializable, new()
{
var reference = new T();
_callbacks[GetHash<T>()] = (reader, userData) =>
{
reference.Deserialize(reader);
onReceive(reference);
};
}
/// <summary>
/// Remove any subscriptions by type
/// </summary>
/// <typeparam name="T">Packet type</typeparam>
/// <returns>true if remove is success</returns>
public bool RemoveSubscription<T>()
{
return _callbacks.Remove(GetHash<T>());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f0d5a653362556943a36db010a601057
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,738 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Net;
using System.Runtime.Serialization;
namespace LiteNetLib.Utils
{
public class InvalidTypeException : ArgumentException
{
public InvalidTypeException(string message) : base(message) { }
}
public class ParseException : Exception
{
public ParseException(string message) : base(message) { }
}
public class NetSerializer
{
private enum CallType
{
Basic,
Array,
List
}
private abstract class FastCall<T>
{
public CallType Type;
public virtual void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { Type = type; }
public abstract void Read(T inf, NetDataReader r);
public abstract void Write(T inf, NetDataWriter w);
public abstract void ReadArray(T inf, NetDataReader r);
public abstract void WriteArray(T inf, NetDataWriter w);
public abstract void ReadList(T inf, NetDataReader r);
public abstract void WriteList(T inf, NetDataWriter w);
}
private abstract class FastCallSpecific<TClass, TProperty> : FastCall<TClass>
{
protected Func<TClass, TProperty> Getter;
protected Action<TClass, TProperty> Setter;
protected Func<TClass, TProperty[]> GetterArr;
protected Action<TClass, TProperty[]> SetterArr;
protected Func<TClass, List<TProperty>> GetterList;
protected Action<TClass, List<TProperty>> SetterList;
public override void ReadArray(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); }
public override void WriteArray(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); }
public override void ReadList(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); }
public override void WriteList(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); }
protected TProperty[] ReadArrayHelper(TClass inf, NetDataReader r)
{
ushort count = r.GetUShort();
var arr = GetterArr(inf);
arr = arr == null || arr.Length != count ? new TProperty[count] : arr;
SetterArr(inf, arr);
return arr;
}
protected TProperty[] WriteArrayHelper(TClass inf, NetDataWriter w)
{
var arr = GetterArr(inf);
w.Put((ushort)arr.Length);
return arr;
}
protected List<TProperty> ReadListHelper(TClass inf, NetDataReader r, out int len)
{
len = r.GetUShort();
var list = GetterList(inf);
if (list == null)
{
list = new List<TProperty>(len);
SetterList(inf, list);
}
return list;
}
protected List<TProperty> WriteListHelper(TClass inf, NetDataWriter w, out int len)
{
var list = GetterList(inf);
if (list == null)
{
len = 0;
w.Put(0);
return null;
}
len = list.Count;
w.Put((ushort)len);
return list;
}
public override void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type)
{
base.Init(getMethod, setMethod, type);
switch (type)
{
case CallType.Array:
GetterArr = (Func<TClass, TProperty[]>)Delegate.CreateDelegate(typeof(Func<TClass, TProperty[]>), getMethod);
SetterArr = (Action<TClass, TProperty[]>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty[]>), setMethod);
break;
case CallType.List:
GetterList = (Func<TClass, List<TProperty>>)Delegate.CreateDelegate(typeof(Func<TClass, List<TProperty>>), getMethod);
SetterList = (Action<TClass, List<TProperty>>)Delegate.CreateDelegate(typeof(Action<TClass, List<TProperty>>), setMethod);
break;
default:
Getter = (Func<TClass, TProperty>)Delegate.CreateDelegate(typeof(Func<TClass, TProperty>), getMethod);
Setter = (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), setMethod);
break;
}
}
}
private abstract class FastCallSpecificAuto<TClass, TProperty> : FastCallSpecific<TClass, TProperty>
{
protected abstract void ElementRead(NetDataReader r, out TProperty prop);
protected abstract void ElementWrite(NetDataWriter w, ref TProperty prop);
public override void Read(TClass inf, NetDataReader r)
{
ElementRead(r, out var elem);
Setter(inf, elem);
}
public override void Write(TClass inf, NetDataWriter w)
{
var elem = Getter(inf);
ElementWrite(w, ref elem);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
for (int i = 0; i < arr.Length; i++)
ElementRead(r, out arr[i]);
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
for (int i = 0; i < arr.Length; i++)
ElementWrite(w, ref arr[i]);
}
}
private sealed class FastCallStatic<TClass, TProperty> : FastCallSpecific<TClass, TProperty>
{
private readonly Action<NetDataWriter, TProperty> _writer;
private readonly Func<NetDataReader, TProperty> _reader;
public FastCallStatic(Action<NetDataWriter, TProperty> write, Func<NetDataReader, TProperty> read)
{
_writer = write;
_reader = read;
}
public override void Read(TClass inf, NetDataReader r) { Setter(inf, _reader(r)); }
public override void Write(TClass inf, NetDataWriter w) { _writer(w, Getter(inf)); }
public override void ReadList(TClass inf, NetDataReader r)
{
var list = ReadListHelper(inf, r, out int len);
int listCount = list.Count;
for (int i = 0; i < len; i++)
{
if (i < listCount)
list[i] = _reader(r);
else
list.Add(_reader(r));
}
if (len < listCount)
list.RemoveRange(len, listCount - len);
}
public override void WriteList(TClass inf, NetDataWriter w)
{
var list = WriteListHelper(inf, w, out int len);
for (int i = 0; i < len; i++)
_writer(w, list[i]);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i] = _reader(r);
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
int len = arr.Length;
for (int i = 0; i < len; i++)
_writer(w, arr[i]);
}
}
private sealed class FastCallStruct<TClass, TProperty> : FastCallSpecific<TClass, TProperty> where TProperty : struct, INetSerializable
{
private TProperty _p;
public override void Read(TClass inf, NetDataReader r)
{
_p.Deserialize(r);
Setter(inf, _p);
}
public override void Write(TClass inf, NetDataWriter w)
{
_p = Getter(inf);
_p.Serialize(w);
}
public override void ReadList(TClass inf, NetDataReader r)
{
var list = ReadListHelper(inf, r, out int len);
int listCount = list.Count;
for (int i = 0; i < len; i++)
{
var itm = default(TProperty);
itm.Deserialize(r);
if(i < listCount)
list[i] = itm;
else
list.Add(itm);
}
if (len < listCount)
list.RemoveRange(len, listCount - len);
}
public override void WriteList(TClass inf, NetDataWriter w)
{
var list = WriteListHelper(inf, w, out int len);
for (int i = 0; i < len; i++)
list[i].Serialize(w);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i].Deserialize(r);
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i].Serialize(w);
}
}
private sealed class FastCallClass<TClass, TProperty> : FastCallSpecific<TClass, TProperty> where TProperty : class, INetSerializable
{
private readonly Func<TProperty> _constructor;
public FastCallClass(Func<TProperty> constructor) { _constructor = constructor; }
public override void Read(TClass inf, NetDataReader r)
{
var p = _constructor();
p.Deserialize(r);
Setter(inf, p);
}
public override void Write(TClass inf, NetDataWriter w)
{
var p = Getter(inf);
p?.Serialize(w);
}
public override void ReadList(TClass inf, NetDataReader r)
{
var list = ReadListHelper(inf, r, out int len);
int listCount = list.Count;
for (int i = 0; i < len; i++)
{
if (i < listCount)
{
list[i].Deserialize(r);
}
else
{
var itm = _constructor();
itm.Deserialize(r);
list.Add(itm);
}
}
if (len < listCount)
list.RemoveRange(len, listCount - len);
}
public override void WriteList(TClass inf, NetDataWriter w)
{
var list = WriteListHelper(inf, w, out int len);
for (int i = 0; i < len; i++)
list[i].Serialize(w);
}
public override void ReadArray(TClass inf, NetDataReader r)
{
var arr = ReadArrayHelper(inf, r);
int len = arr.Length;
for (int i = 0; i < len; i++)
{
arr[i] = _constructor();
arr[i].Deserialize(r);
}
}
public override void WriteArray(TClass inf, NetDataWriter w)
{
var arr = WriteArrayHelper(inf, w);
int len = arr.Length;
for (int i = 0; i < len; i++)
arr[i].Serialize(w);
}
}
private class IntSerializer<T> : FastCallSpecific<T, int>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetInt()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetIntArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class UIntSerializer<T> : FastCallSpecific<T, uint>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUInt()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUIntArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class ShortSerializer<T> : FastCallSpecific<T, short>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetShort()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetShortArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class UShortSerializer<T> : FastCallSpecific<T, ushort>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUShort()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUShortArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class LongSerializer<T> : FastCallSpecific<T, long>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetLong()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetLongArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class ULongSerializer<T> : FastCallSpecific<T, ulong>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetULong()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetULongArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class ByteSerializer<T> : FastCallSpecific<T, byte>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetByte()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBytesWithLength()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutBytesWithLength(GetterArr(inf)); }
}
private class SByteSerializer<T> : FastCallSpecific<T, sbyte>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetSByte()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetSBytesWithLength()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutSBytesWithLength(GetterArr(inf)); }
}
private class FloatSerializer<T> : FastCallSpecific<T, float>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetFloat()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetFloatArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class DoubleSerializer<T> : FastCallSpecific<T, double>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetDouble()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetDoubleArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class BoolSerializer<T> : FastCallSpecific<T, bool>
{
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetBool()); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBoolArray()); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); }
}
private class CharSerializer<T> : FastCallSpecificAuto<T, char>
{
protected override void ElementWrite(NetDataWriter w, ref char prop) { w.Put(prop); }
protected override void ElementRead(NetDataReader r, out char prop) { prop = r.GetChar(); }
}
private class IPEndPointSerializer<T> : FastCallSpecificAuto<T, IPEndPoint>
{
protected override void ElementWrite(NetDataWriter w, ref IPEndPoint prop) { w.Put(prop); }
protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); }
}
private class StringSerializer<T> : FastCallSpecific<T, string>
{
private readonly int _maxLength;
public StringSerializer(int maxLength) { _maxLength = maxLength > 0 ? maxLength : short.MaxValue; }
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetString(_maxLength)); }
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf), _maxLength); }
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetStringArray(_maxLength)); }
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf), _maxLength); }
}
private class EnumByteSerializer<T> : FastCall<T>
{
protected readonly PropertyInfo Property;
protected readonly Type PropertyType;
public EnumByteSerializer(PropertyInfo property, Type propertyType)
{
Property = property;
PropertyType = propertyType;
}
public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetByte()), null); }
public override void Write(T inf, NetDataWriter w) { w.Put((byte)Property.GetValue(inf, null)); }
public override void ReadArray(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: Enum[]"); }
public override void WriteArray(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: Enum[]"); }
public override void ReadList(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<Enum>"); }
public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<Enum>"); }
}
private class EnumIntSerializer<T> : EnumByteSerializer<T>
{
public EnumIntSerializer(PropertyInfo property, Type propertyType) : base(property, propertyType) { }
public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetInt()), null); }
public override void Write(T inf, NetDataWriter w) { w.Put((int)Property.GetValue(inf, null)); }
}
private sealed class ClassInfo<T>
{
public static ClassInfo<T> Instance;
private readonly FastCall<T>[] _serializers;
private readonly int _membersCount;
public ClassInfo(List<FastCall<T>> serializers)
{
_membersCount = serializers.Count;
_serializers = serializers.ToArray();
}
public void Write(T obj, NetDataWriter writer)
{
for (int i = 0; i < _membersCount; i++)
{
var s = _serializers[i];
if (s.Type == CallType.Basic)
s.Write(obj, writer);
else if (s.Type == CallType.Array)
s.WriteArray(obj, writer);
else
s.WriteList(obj, writer);
}
}
public void Read(T obj, NetDataReader reader)
{
for (int i = 0; i < _membersCount; i++)
{
var s = _serializers[i];
if (s.Type == CallType.Basic)
s.Read(obj, reader);
else if(s.Type == CallType.Array)
s.ReadArray(obj, reader);
else
s.ReadList(obj, reader);
}
}
}
private abstract class CustomType
{
public abstract FastCall<T> Get<T>();
}
private sealed class CustomTypeStruct<TProperty> : CustomType where TProperty : struct, INetSerializable
{
public override FastCall<T> Get<T>() { return new FastCallStruct<T, TProperty>(); }
}
private sealed class CustomTypeClass<TProperty> : CustomType where TProperty : class, INetSerializable
{
private readonly Func<TProperty> _constructor;
public CustomTypeClass(Func<TProperty> constructor) { _constructor = constructor; }
public override FastCall<T> Get<T>() { return new FastCallClass<T, TProperty>(_constructor); }
}
private sealed class CustomTypeStatic<TProperty> : CustomType
{
private readonly Action<NetDataWriter, TProperty> _writer;
private readonly Func<NetDataReader, TProperty> _reader;
public CustomTypeStatic(Action<NetDataWriter, TProperty> writer, Func<NetDataReader, TProperty> reader)
{
_writer = writer;
_reader = reader;
}
public override FastCall<T> Get<T>() { return new FastCallStatic<T, TProperty>(_writer, _reader); }
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">INetSerializable structure</typeparam>
public void RegisterNestedType<T>() where T : struct, INetSerializable
{
_registeredTypes.Add(typeof(T), new CustomTypeStruct<T>());
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">INetSerializable class</typeparam>
public void RegisterNestedType<T>(Func<T> constructor) where T : class, INetSerializable
{
_registeredTypes.Add(typeof(T), new CustomTypeClass<T>(constructor));
}
/// <summary>
/// Register custom property type
/// </summary>
/// <typeparam name="T">Any packet</typeparam>
/// <param name="writer">custom type writer</param>
/// <param name="reader">custom type reader</param>
public void RegisterNestedType<T>(Action<NetDataWriter, T> writer, Func<NetDataReader, T> reader)
{
_registeredTypes.Add(typeof(T), new CustomTypeStatic<T>(writer, reader));
}
private NetDataWriter _writer;
private readonly int _maxStringLength;
private readonly Dictionary<Type, CustomType> _registeredTypes = new Dictionary<Type, CustomType>();
public NetSerializer() : this(0)
{
}
public NetSerializer(int maxStringLength)
{
_maxStringLength = maxStringLength;
}
private ClassInfo<T> RegisterInternal<T>()
{
if (ClassInfo<T>.Instance != null)
return ClassInfo<T>.Instance;
Type t = typeof(T);
var props = t.GetProperties(
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.GetProperty |
BindingFlags.SetProperty);
var serializers = new List<FastCall<T>>();
for (int i = 0; i < props.Length; i++)
{
var property = props[i];
var propertyType = property.PropertyType;
var elementType = propertyType.IsArray ? propertyType.GetElementType() : propertyType;
var callType = propertyType.IsArray ? CallType.Array : CallType.Basic;
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>))
{
elementType = propertyType.GetGenericArguments()[0];
callType = CallType.List;
}
if (Attribute.IsDefined(property, typeof(IgnoreDataMemberAttribute)))
continue;
var getMethod = property.GetGetMethod();
var setMethod = property.GetSetMethod();
if (getMethod == null || setMethod == null)
continue;
FastCall<T> serialzer = null;
if (propertyType.IsEnum)
{
var underlyingType = Enum.GetUnderlyingType(propertyType);
if (underlyingType == typeof(byte))
serialzer = new EnumByteSerializer<T>(property, propertyType);
else if (underlyingType == typeof(int))
serialzer = new EnumIntSerializer<T>(property, propertyType);
else
throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name);
}
else if (elementType == typeof(string))
serialzer = new StringSerializer<T>(_maxStringLength);
else if (elementType == typeof(bool))
serialzer = new BoolSerializer<T>();
else if (elementType == typeof(byte))
serialzer = new ByteSerializer<T>();
else if (elementType == typeof(sbyte))
serialzer = new SByteSerializer<T>();
else if (elementType == typeof(short))
serialzer = new ShortSerializer<T>();
else if (elementType == typeof(ushort))
serialzer = new UShortSerializer<T>();
else if (elementType == typeof(int))
serialzer = new IntSerializer<T>();
else if (elementType == typeof(uint))
serialzer = new UIntSerializer<T>();
else if (elementType == typeof(long))
serialzer = new LongSerializer<T>();
else if (elementType == typeof(ulong))
serialzer = new ULongSerializer<T>();
else if (elementType == typeof(float))
serialzer = new FloatSerializer<T>();
else if (elementType == typeof(double))
serialzer = new DoubleSerializer<T>();
else if (elementType == typeof(char))
serialzer = new CharSerializer<T>();
else if (elementType == typeof(IPEndPoint))
serialzer = new IPEndPointSerializer<T>();
else
{
_registeredTypes.TryGetValue(elementType, out var customType);
if (customType != null)
serialzer = customType.Get<T>();
}
if (serialzer != null)
{
serialzer.Init(getMethod, setMethod, callType);
serializers.Add(serialzer);
}
else
{
throw new InvalidTypeException("Unknown property type: " + propertyType.FullName);
}
}
ClassInfo<T>.Instance = new ClassInfo<T>(serializers);
return ClassInfo<T>.Instance;
}
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Register<T>()
{
RegisterInternal<T>();
}
/// <summary>
/// Reads packet with known type
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <returns>Returns packet if packet in reader is matched type</returns>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public T Deserialize<T>(NetDataReader reader) where T : class, new()
{
var info = RegisterInternal<T>();
var result = new T();
try
{
info.Read(result, reader);
}
catch
{
return null;
}
return result;
}
/// <summary>
/// Reads packet with known type (non alloc variant)
/// </summary>
/// <param name="reader">NetDataReader with packet</param>
/// <param name="target">Deserialization target</param>
/// <returns>Returns true if packet in reader is matched type</returns>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public bool Deserialize<T>(NetDataReader reader, T target) where T : class, new()
{
var info = RegisterInternal<T>();
try
{
info.Read(target, reader);
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Serialize object to NetDataWriter (fast)
/// </summary>
/// <param name="writer">Serialization target NetDataWriter</param>
/// <param name="obj">Object to serialize</param>
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
public void Serialize<T>(NetDataWriter writer, T obj) where T : class, new()
{
RegisterInternal<T>().Write(obj, writer);
}
/// <summary>
/// Serialize object to byte array
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>byte array with serialized data</returns>
public byte[] Serialize<T>(T obj) where T : class, new()
{
if (_writer == null)
_writer = new NetDataWriter();
_writer.Reset();
Serialize(_writer, obj);
return _writer.CopyData();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 503136cdfd845ea439d6eb1e7fcfa924
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,423 @@
using System;
namespace LiteNetLib.Utils
{
/// <summary>
/// Represents RFC4330 SNTP packet used for communication to and from a network time server.
/// </summary>
/// <remarks>
/// <para>
/// Most applications should just use the <see cref="NtpPacket.CorrectionOffset" /> property.
/// </para>
/// <para>
/// The same data structure represents both request and reply packets.
/// Request and reply differ in which properties are set and to what values.
/// </para>
/// <para>
/// The only real property is <see cref="NtpPacket.Bytes" />.
/// All other properties read from and write to the underlying byte array
/// with the exception of <see cref="NtpPacket.DestinationTimestamp" />,
/// which is not part of the packet on network and it is instead set locally after receiving the packet.
/// </para>
/// <para>
/// Copied from <a href="https://guerrillantp.machinezoo.com/">GuerrillaNtp project</a>
/// with permission from Robert Vazan (@robertvazan) under MIT license, see https://github.com/RevenantX/LiteNetLib/pull/236
/// </para>
/// </remarks>
public class NtpPacket
{
private static readonly DateTime Epoch = new DateTime(1900, 1, 1);
/// <summary>
/// Gets RFC4330-encoded SNTP packet.
/// </summary>
/// <value>
/// Byte array containing RFC4330-encoded SNTP packet. It is at least 48 bytes long.
/// </value>
/// <remarks>
/// This is the only real property. All other properties except
/// <see cref="NtpPacket.DestinationTimestamp" /> read from or write to this byte array.
/// </remarks>
public byte[] Bytes { get; }
/// <summary>
/// Gets the leap second indicator.
/// </summary>
/// <value>
/// Leap second warning, if any. Special value
/// <see cref="NtpLeapIndicator.AlarmCondition" /> indicates unsynchronized server clock.
/// Default is <see cref="NtpLeapIndicator.NoWarning" />.
/// </value>
/// <remarks>
/// Only servers fill in this property. Clients can consult this property for possible leap second warning.
/// </remarks>
public NtpLeapIndicator LeapIndicator => (NtpLeapIndicator)((Bytes[0] & 0xC0) >> 6);
/// <summary>
/// Gets or sets protocol version number.
/// </summary>
/// <value>
/// SNTP protocol version. Default is 4, which is the latest version at the time of this writing.
/// </value>
/// <remarks>
/// In request packets, clients should leave this property at default value 4.
/// Servers usually reply with the same protocol version.
/// </remarks>
public int VersionNumber
{
get => (Bytes[0] & 0x38) >> 3;
private set => Bytes[0] = (byte)((Bytes[0] & ~0x38) | value << 3);
}
/// <summary>
/// Gets or sets SNTP packet mode, i.e. whether this is client or server packet.
/// </summary>
/// <value>
/// SNTP packet mode. Default is <see cref="NtpMode.Client" /> in newly created packets.
/// Server reply should have this property set to <see cref="NtpMode.Server" />.
/// </value>
public NtpMode Mode
{
get => (NtpMode)(Bytes[0] & 0x07);
private set => Bytes[0] = (byte)((Bytes[0] & ~0x07) | (int)value);
}
/// <summary>
/// Gets server's distance from the reference clock.
/// </summary>
/// <value>
/// <para>
/// Distance from the reference clock. This property is set only in server reply packets.
/// Servers connected directly to reference clock hardware set this property to 1.
/// Statum number is incremented by 1 on every hop down the NTP server hierarchy.
/// </para>
/// <para>
/// Special value 0 indicates that this packet is a Kiss-o'-Death message
/// with kiss code stored in <see cref="NtpPacket.ReferenceId" />.
/// </para>
/// </value>
public int Stratum => Bytes[1];
/// <summary>
/// Gets server's preferred polling interval.
/// </summary>
/// <value>
/// Polling interval in log2 seconds, e.g. 4 stands for 16s and 17 means 131,072s.
/// </value>
public int Poll => Bytes[2];
/// <summary>
/// Gets the precision of server clock.
/// </summary>
/// <value>
/// Clock precision in log2 seconds, e.g. -20 for microsecond precision.
/// </value>
public int Precision => (sbyte)Bytes[3];
/// <summary>
/// Gets the total round-trip delay from the server to the reference clock.
/// </summary>
/// <value>
/// Round-trip delay to the reference clock. Normally a positive value smaller than one second.
/// </value>
public TimeSpan RootDelay => GetTimeSpan32(4);
/// <summary>
/// Gets the estimated error in time reported by the server.
/// </summary>
/// <value>
/// Estimated error in time reported by the server. Normally a positive value smaller than one second.
/// </value>
public TimeSpan RootDispersion => GetTimeSpan32(8);
/// <summary>
/// Gets the ID of the time source used by the server or Kiss-o'-Death code sent by the server.
/// </summary>
/// <value>
/// <para>
/// ID of server's time source or Kiss-o'-Death code.
/// Purpose of this property depends on value of <see cref="NtpPacket.Stratum" /> property.
/// </para>
/// <para>
/// Stratum 1 servers write here one of several special values that describe the kind of hardware clock they use.
/// </para>
/// <para>
/// Stratum 2 and lower servers set this property to IPv4 address of their upstream server.
/// If upstream server has IPv6 address, the address is hashed, because it doesn't fit in this property.
/// </para>
/// <para>
/// When server sets <see cref="NtpPacket.Stratum" /> to special value 0,
/// this property contains so called kiss code that instructs the client to stop querying the server.
/// </para>
/// </value>
public uint ReferenceId => GetUInt32BE(12);
/// <summary>
/// Gets or sets the time when the server clock was last set or corrected.
/// </summary>
/// <value>
/// Time when the server clock was last set or corrected or <c>null</c> when not specified.
/// </value>
/// <remarks>
/// This Property is usually set only by servers. It usually lags server's current time by several minutes,
/// so don't use this property for time synchronization.
/// </remarks>
public DateTime? ReferenceTimestamp => GetDateTime64(16);
/// <summary>
/// Gets or sets the time when the client sent its request.
/// </summary>
/// <value>
/// This property is <c>null</c> in request packets.
/// In reply packets, it is the time when the client sent its request.
/// Servers copy this value from <see cref="NtpPacket.TransmitTimestamp" />
/// that they find in received request packet.
/// </value>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? OriginTimestamp => GetDateTime64(24);
/// <summary>
/// Gets or sets the time when the request was received by the server.
/// </summary>
/// <value>
/// This property is <c>null</c> in request packets.
/// In reply packets, it is the time when the server received client request.
/// </value>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? ReceiveTimestamp => GetDateTime64(32);
/// <summary>
/// Gets or sets the time when the packet was sent.
/// </summary>
/// <value>
/// Time when the packet was sent. It should never be <c>null</c>.
/// Default value is <see cref="System.DateTime.UtcNow" />.
/// </value>
/// <remarks>
/// This property must be set by both clients and servers.
/// </remarks>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? TransmitTimestamp { get { return GetDateTime64(40); } private set { SetDateTime64(40, value); } }
/// <summary>
/// Gets or sets the time of reception of response SNTP packet on the client.
/// </summary>
/// <value>
/// Time of reception of response SNTP packet on the client. It is <c>null</c> in request packets.
/// </value>
/// <remarks>
/// This property is not part of the protocol and has to be set when reply packet is received.
/// </remarks>
/// <seealso cref="NtpPacket.CorrectionOffset" />
/// <seealso cref="NtpPacket.RoundTripTime" />
public DateTime? DestinationTimestamp { get; private set; }
/// <summary>
/// Gets the round-trip time to the server.
/// </summary>
/// <value>
/// Time the request spent traveling to the server plus the time the reply spent traveling back.
/// This is calculated from timestamps in the packet as <c>(t1 - t0) + (t3 - t2)</c>
/// where t0 is <see cref="NtpPacket.OriginTimestamp" />,
/// t1 is <see cref="NtpPacket.ReceiveTimestamp" />,
/// t2 is <see cref="NtpPacket.TransmitTimestamp" />,
/// and t3 is <see cref="NtpPacket.DestinationTimestamp" />.
/// This property throws an exception in request packets.
/// </value>
public TimeSpan RoundTripTime
{
get
{
CheckTimestamps();
return (ReceiveTimestamp.Value - OriginTimestamp.Value) + (DestinationTimestamp.Value - TransmitTimestamp.Value);
}
}
/// <summary>
/// Gets the offset that should be added to local time to synchronize it with server time.
/// </summary>
/// <value>
/// Time difference between server and client. It should be added to local time to get server time.
/// It is calculated from timestamps in the packet as <c>0.5 * ((t1 - t0) - (t3 - t2))</c>
/// where t0 is <see cref="NtpPacket.OriginTimestamp" />,
/// t1 is <see cref="NtpPacket.ReceiveTimestamp" />,
/// t2 is <see cref="NtpPacket.TransmitTimestamp" />,
/// and t3 is <see cref="NtpPacket.DestinationTimestamp" />.
/// This property throws an exception in request packets.
/// </value>
public TimeSpan CorrectionOffset
{
get
{
CheckTimestamps();
return TimeSpan.FromTicks(((ReceiveTimestamp.Value - OriginTimestamp.Value) - (DestinationTimestamp.Value - TransmitTimestamp.Value)).Ticks / 2);
}
}
/// <summary>
/// Initializes default request packet.
/// </summary>
/// <remarks>
/// Properties <see cref="NtpPacket.Mode" /> and <see cref="NtpPacket.VersionNumber" />
/// are set appropriately for request packet. Property <see cref="NtpPacket.TransmitTimestamp" />
/// is set to <see cref="System.DateTime.UtcNow" />.
/// </remarks>
public NtpPacket() : this(new byte[48])
{
Mode = NtpMode.Client;
VersionNumber = 4;
TransmitTimestamp = DateTime.UtcNow;
}
/// <summary>
/// Initializes packet from received data.
/// </summary>
internal NtpPacket(byte[] bytes)
{
if (bytes.Length < 48)
throw new ArgumentException("SNTP reply packet must be at least 48 bytes long.", "bytes");
Bytes = bytes;
}
/// <summary>
/// Initializes packet from data received from a server.
/// </summary>
/// <param name="bytes">Data received from the server.</param>
/// <param name="destinationTimestamp">Utc time of reception of response SNTP packet on the client.</param>
/// <returns></returns>
public static NtpPacket FromServerResponse(byte[] bytes, DateTime destinationTimestamp)
{
return new NtpPacket(bytes) { DestinationTimestamp = destinationTimestamp };
}
internal void ValidateRequest()
{
if (Mode != NtpMode.Client)
throw new InvalidOperationException("This is not a request SNTP packet.");
if (VersionNumber == 0)
throw new InvalidOperationException("Protocol version of the request is not specified.");
if (TransmitTimestamp == null)
throw new InvalidOperationException("TransmitTimestamp must be set in request packet.");
}
internal void ValidateReply()
{
if (Mode != NtpMode.Server)
throw new InvalidOperationException("This is not a reply SNTP packet.");
if (VersionNumber == 0)
throw new InvalidOperationException("Protocol version of the reply is not specified.");
if (Stratum == 0)
throw new InvalidOperationException(string.Format("Received Kiss-o'-Death SNTP packet with code 0x{0:x}.", ReferenceId));
if (LeapIndicator == NtpLeapIndicator.AlarmCondition)
throw new InvalidOperationException("SNTP server has unsynchronized clock.");
CheckTimestamps();
}
private void CheckTimestamps()
{
if (OriginTimestamp == null)
throw new InvalidOperationException("Origin timestamp is missing.");
if (ReceiveTimestamp == null)
throw new InvalidOperationException("Receive timestamp is missing.");
if (TransmitTimestamp == null)
throw new InvalidOperationException("Transmit timestamp is missing.");
if (DestinationTimestamp == null)
throw new InvalidOperationException("Destination timestamp is missing.");
}
private DateTime? GetDateTime64(int offset)
{
var field = GetUInt64BE(offset);
if (field == 0)
return null;
return new DateTime(Epoch.Ticks + Convert.ToInt64(field * (1.0 / (1L << 32) * 10000000.0)));
}
private void SetDateTime64(int offset, DateTime? value)
{
SetUInt64BE(offset, value == null ? 0 : Convert.ToUInt64((value.Value.Ticks - Epoch.Ticks) * (0.0000001 * (1L << 32))));
}
private TimeSpan GetTimeSpan32(int offset)
{
return TimeSpan.FromSeconds(GetInt32BE(offset) / (double)(1 << 16));
}
private ulong GetUInt64BE(int offset)
{
return SwapEndianness(BitConverter.ToUInt64(Bytes, offset));
}
private void SetUInt64BE(int offset, ulong value)
{
FastBitConverter.GetBytes(Bytes, offset, SwapEndianness(value));
}
private int GetInt32BE(int offset)
{
return (int)GetUInt32BE(offset);
}
private uint GetUInt32BE(int offset)
{
return SwapEndianness(BitConverter.ToUInt32(Bytes, offset));
}
private static uint SwapEndianness(uint x)
{
return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24);
}
private static ulong SwapEndianness(ulong x)
{
return ((ulong)SwapEndianness((uint)x) << 32) | SwapEndianness((uint)(x >> 32));
}
}
/// <summary>
/// Represents leap second warning from the server that instructs the client to add or remove leap second.
/// </summary>
/// <seealso cref="NtpPacket.LeapIndicator" />
public enum NtpLeapIndicator
{
/// <summary>
/// No leap second warning. No action required.
/// </summary>
NoWarning,
/// <summary>
/// Warns the client that the last minute of the current day has 61 seconds.
/// </summary>
LastMinuteHas61Seconds,
/// <summary>
/// Warns the client that the last minute of the current day has 59 seconds.
/// </summary>
LastMinuteHas59Seconds,
/// <summary>
/// Special value indicating that the server clock is unsynchronized and the returned time is unreliable.
/// </summary>
AlarmCondition
}
/// <summary>
/// Describes SNTP packet mode, i.e. client or server.
/// </summary>
/// <seealso cref="NtpPacket.Mode" />
public enum NtpMode
{
/// <summary>
/// Identifies client-to-server SNTP packet.
/// </summary>
Client = 3,
/// <summary>
/// Identifies server-to-client SNTP packet.
/// </summary>
Server = 4,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: daf31cf4ab8132943b2c8ca301fc919a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
using System.Net;
using System.Net.Sockets;
namespace LiteNetLib.Utils
{
internal sealed class NtpRequest
{
private const int ResendTimer = 1000;
private const int KillTimer = 10000;
public const int DefaultPort = 123;
private readonly IPEndPoint _ntpEndPoint;
private int _resendTime = ResendTimer;
private int _killTime = 0;
public NtpRequest(IPEndPoint endPoint)
{
_ntpEndPoint = endPoint;
}
public bool NeedToKill => _killTime >= KillTimer;
public bool Send(Socket socket, int time)
{
_resendTime += time;
_killTime += time;
if (_resendTime < ResendTimer)
{
return false;
}
var packet = new NtpPacket();
try
{
int sendCount = socket.SendTo(packet.Bytes, 0, packet.Bytes.Length, SocketFlags.None, _ntpEndPoint);
return sendCount == packet.Bytes.Length;
}
catch
{
return false;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3fa3bfbb02dd944e939070e3cf0638c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,505 @@
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Managing.Transporting;
using LiteNetLib;
using LiteNetLib.Layers;
using System;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Transporting.Tugboat
{
[DisallowMultipleComponent]
[AddComponentMenu("FishNet/Transport/Tugboat")]
public class Tugboat : Transport
{
~Tugboat()
{
Shutdown();
}
#region Serialized.
[Header("Channels")]
/// <summary>
/// Maximum transmission unit for the unreliable channel.
/// </summary>
[Tooltip("Maximum transmission unit for the unreliable channel.")]
[Range(MINIMUM_UDP_MTU, MAXIMUM_UDP_MTU)]
[SerializeField]
private int _unreliableMTU = 1023;
[Header("Server")]
/// <summary>
/// IPv4 address to bind server to.
/// </summary>
[Tooltip("IPv4 Address to bind server to.")]
[SerializeField]
private string _ipv4BindAddress;
/// <summary>
/// IPv6 address to bind server to.
/// </summary>
[Tooltip("IPv6 Address to bind server to.")]
[SerializeField]
private string _ipv6BindAddress;
/// <summary>
/// Port to use.
/// </summary>
[Tooltip("Port to use.")]
[SerializeField]
private ushort _port = 7770;
/// <summary>
/// Maximum number of players which may be connected at once.
/// </summary>
[Tooltip("Maximum number of players which may be connected at once.")]
[Range(1, 9999)]
[SerializeField]
private int _maximumClients = 4095;
[Header("Client")]
/// <summary>
/// Address to connect.
/// </summary>
[Tooltip("Address to connect.")]
[SerializeField]
private string _clientAddress = "localhost";
[Header("Misc")]
/// <summary>
/// How long in seconds until either the server or client socket must go without data before being timed out. Use 0f to disable timing out.
/// </summary>
[Tooltip("How long in seconds until either the server or client socket must go without data before being timed out. Use 0f to disable timing out.")]
[Range(0, MAX_TIMEOUT_SECONDS)]
[SerializeField]
private ushort _timeout = 15;
#endregion
#region Private.
/// <summary>
/// PacketLayer to use with LiteNetLib.
/// </summary>
private PacketLayerBase _packetLayer;
/// <summary>
/// Server socket and handler.
/// </summary>
private Server.ServerSocket _server = new Server.ServerSocket();
/// <summary>
/// Client socket and handler.
/// </summary>
private Client.ClientSocket _client = new Client.ClientSocket();
#endregion
#region Const.
private const ushort MAX_TIMEOUT_SECONDS = 1800;
/// <summary>
/// Minimum UDP packet size allowed.
/// </summary>
private const int MINIMUM_UDP_MTU = 576;
/// <summary>
/// Maximum UDP packet size allowed.
/// </summary>
private const int MAXIMUM_UDP_MTU = 1023;
#endregion
#region Initialization and unity.
public override void Initialize(NetworkManager networkManager, int transportIndex)
{
base.Initialize(networkManager, transportIndex);
}
protected void OnDestroy()
{
Shutdown();
}
#endregion
#region ConnectionStates.
/// <summary>
/// Gets the address of a remote connection Id.
/// </summary>
/// <param name="connectionId"></param>
/// <returns></returns>
public override string GetConnectionAddress(int connectionId)
{
return _server.GetConnectionAddress(connectionId);
}
/// <summary>
/// Called when a connection state changes for the local client.
/// </summary>
public override event Action<ClientConnectionStateArgs> OnClientConnectionState;
/// <summary>
/// Called when a connection state changes for the local server.
/// </summary>
public override event Action<ServerConnectionStateArgs> OnServerConnectionState;
/// <summary>
/// Called when a connection state changes for a remote client.
/// </summary>
public override event Action<RemoteConnectionStateArgs> OnRemoteConnectionState;
/// <summary>
/// Gets the current local ConnectionState.
/// </summary>
/// <param name="server">True if getting ConnectionState for the server.</param>
public override LocalConnectionState GetConnectionState(bool server)
{
if (server)
return _server.GetConnectionState();
else
return _client.GetConnectionState();
}
/// <summary>
/// Gets the current ConnectionState of a remote client on the server.
/// </summary>
/// <param name="connectionId">ConnectionId to get ConnectionState for.</param>
public override RemoteConnectionState GetConnectionState(int connectionId)
{
return _server.GetConnectionState(connectionId);
}
/// <summary>
/// Handles a ConnectionStateArgs for the local client.
/// </summary>
/// <param name="connectionStateArgs"></param>
public override void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs)
{
OnClientConnectionState?.Invoke(connectionStateArgs);
}
/// <summary>
/// Handles a ConnectionStateArgs for the local server.
/// </summary>
/// <param name="connectionStateArgs"></param>
public override void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs)
{
OnServerConnectionState?.Invoke(connectionStateArgs);
UpdateTimeout();
}
/// <summary>
/// Handles a ConnectionStateArgs for a remote client.
/// </summary>
/// <param name="connectionStateArgs"></param>
public override void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs)
{
OnRemoteConnectionState?.Invoke(connectionStateArgs);
}
#endregion
#region Iterating.
/// <summary>
/// Processes data received by the socket.
/// </summary>
/// <param name="server">True to process data received on the server.</param>
public override void IterateIncoming(bool server)
{
if (server)
_server.IterateIncoming();
else
_client.IterateIncoming();
}
/// <summary>
/// Processes data to be sent by the socket.
/// </summary>
/// <param name="server">True to process data received on the server.</param>
public override void IterateOutgoing(bool server)
{
if (server)
_server.IterateOutgoing();
else
_client.IterateOutgoing();
}
#endregion
#region ReceivedData.
/// <summary>
/// Called when client receives data.
/// </summary>
public override event Action<ClientReceivedDataArgs> OnClientReceivedData;
/// <summary>
/// Handles a ClientReceivedDataArgs.
/// </summary>
/// <param name="receivedDataArgs"></param>
public override void HandleClientReceivedDataArgs(ClientReceivedDataArgs receivedDataArgs)
{
OnClientReceivedData?.Invoke(receivedDataArgs);
}
/// <summary>
/// Called when server receives data.
/// </summary>
public override event Action<ServerReceivedDataArgs> OnServerReceivedData;
/// <summary>
/// Handles a ClientReceivedDataArgs.
/// </summary>
/// <param name="receivedDataArgs"></param>
public override void HandleServerReceivedDataArgs(ServerReceivedDataArgs receivedDataArgs)
{
OnServerReceivedData?.Invoke(receivedDataArgs);
}
#endregion
#region Sending.
/// <summary>
/// Sends to the server or all clients.
/// </summary>
/// <param name="channelId">Channel to use.</param>
/// <param name="segment">Data to send.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void SendToServer(byte channelId, ArraySegment<byte> segment)
{
SanitizeChannel(ref channelId);
_client.SendToServer(channelId, segment);
}
/// <summary>
/// Sends data to a client.
/// </summary>
/// <param name="channelId"></param>
/// <param name="segment"></param>
/// <param name="connectionId"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
{
SanitizeChannel(ref channelId);
_server.SendToClient(channelId, segment, connectionId);
}
#endregion
#region Configuration.
/// <summary>
/// Sets which PacketLayer to use with LiteNetLib.
/// </summary>
/// <param name="packetLayer"></param>
public void SetPacketLayer(PacketLayerBase packetLayer)
{
_packetLayer = packetLayer;
if (GetConnectionState(true) != LocalConnectionState.Stopped)
base.NetworkManager.LogWarning("PacketLayer is set but will not be applied until the server stops.");
if (GetConnectionState(false) != LocalConnectionState.Stopped)
base.NetworkManager.LogWarning("PacketLayer is set but will not be applied until the client stops.");
_server.Initialize(this, _unreliableMTU, _packetLayer);
_client.Initialize(this, _unreliableMTU, _packetLayer);
}
/// <summary>
/// How long in seconds until either the server or client socket must go without data before being timed out.
/// </summary>
/// <param name="asServer">True to get the timeout for the server socket, false for the client socket.</param>
/// <returns></returns>
public override float GetTimeout(bool asServer)
{
//Server and client uses the same timeout.
return (float)_timeout;
}
/// <summary>
/// Sets how long in seconds until either the server or client socket must go without data before being timed out.
/// </summary>
/// <param name="asServer">True to set the timeout for the server socket, false for the client socket.</param>
public override void SetTimeout(float value, bool asServer)
{
_timeout = (ushort)value;
}
/// <summary>
/// Returns the maximum number of clients allowed to connect to the server. If the transport does not support this method the value -1 is returned.
/// </summary>
/// <returns></returns>
public override int GetMaximumClients()
{
return _server.GetMaximumClients();
}
/// <summary>
/// Sets maximum number of clients allowed to connect to the server. If applied at runtime and clients exceed this value existing clients will stay connected but new clients may not connect.
/// </summary>
/// <param name="value"></param>
public override void SetMaximumClients(int value)
{
_maximumClients = value;
_server.SetMaximumClients(value);
}
/// <summary>
/// Sets which address the client will connect to.
/// </summary>
/// <param name="address"></param>
public override void SetClientAddress(string address)
{
_clientAddress = address;
}
/// <summary>
/// Gets which address the client will connect to.
/// </summary>
public override string GetClientAddress()
{
return _clientAddress;
}
/// <summary>
/// Sets which address the server will bind to.
/// </summary>
/// <param name="address"></param>
public override void SetServerBindAddress(string address, IPAddressType addressType)
{
if (addressType == IPAddressType.IPv4)
_ipv4BindAddress = address;
else
_ipv6BindAddress = address;
}
/// <summary>
/// Gets which address the server will bind to.
/// </summary>
/// <param name="address"></param>
public override string GetServerBindAddress(IPAddressType addressType)
{
if (addressType == IPAddressType.IPv4)
return _ipv4BindAddress;
else
return _ipv6BindAddress;
}
/// <summary>
/// Sets which port to use.
/// </summary>
/// <param name="port"></param>
public override void SetPort(ushort port)
{
_port = port;
}
/// <summary>
/// Gets which port to use.
/// </summary>
/// <param name="port"></param>
public override ushort GetPort()
{
return _port;
}
#endregion
#region Start and stop.
/// <summary>
/// Starts the local server or client using configured settings.
/// </summary>
/// <param name="server">True to start server.</param>
public override bool StartConnection(bool server)
{
if (server)
return StartServer();
else
return StartClient(_clientAddress);
}
/// <summary>
/// Stops the local server or client.
/// </summary>
/// <param name="server">True to stop server.</param>
public override bool StopConnection(bool server)
{
if (server)
return StopServer();
else
return StopClient();
}
/// <summary>
/// Stops a remote client from the server, disconnecting the client.
/// </summary>
/// <param name="connectionId">ConnectionId of the client to disconnect.</param>
/// <param name="immediately">True to abrutly stop the client socket. The technique used to accomplish immediate disconnects may vary depending on the transport.
/// When not using immediate disconnects it's recommended to perform disconnects using the ServerManager rather than accessing the transport directly.
/// </param>
public override bool StopConnection(int connectionId, bool immediately)
{
return _server.StopConnection(connectionId);
}
/// <summary>
/// Stops both client and server.
/// </summary>
public override void Shutdown()
{
//Stops client then server connections.
StopConnection(false);
StopConnection(true);
}
#region Privates.
/// <summary>
/// Starts server.
/// </summary>
private bool StartServer()
{
_server.Initialize(this, _unreliableMTU, _packetLayer);
UpdateTimeout();
return _server.StartConnection(_port, _maximumClients, _ipv4BindAddress, _ipv6BindAddress);
}
/// <summary>
/// Stops server.
/// </summary>
private bool StopServer()
{
return _server.StopConnection();
}
/// <summary>
/// Starts the client.
/// </summary>
/// <param name="address"></param>
private bool StartClient(string address)
{
_client.Initialize(this, _unreliableMTU, _packetLayer);
UpdateTimeout();
return _client.StartConnection(address, _port);
}
/// <summary>
/// Updates clients timeout values.
/// </summary>
private void UpdateTimeout()
{
//If server is running set timeout to max. This is for host only.
//int timeout = (GetConnectionState(true) != LocalConnectionState.Stopped) ? MAX_TIMEOUT_SECONDS : _timeout;
int timeout = (Application.isEditor) ? MAX_TIMEOUT_SECONDS : _timeout;
_client.UpdateTimeout(timeout);
_server.UpdateTimeout(timeout);
}
/// <summary>
/// Stops the client.
/// </summary>
private bool StopClient()
{
return _client.StopConnection();
}
#endregion
#endregion
#region Channels.
/// <summary>
/// If channelId is invalid then channelId becomes forced to reliable.
/// </summary>
/// <param name="channelId"></param>
private void SanitizeChannel(ref byte channelId)
{
if (channelId < 0 || channelId >= TransportManager.CHANNEL_COUNT)
{
NetworkManager.LogWarning($"Channel of {channelId} is out of range of supported channels. Channel will be defaulted to reliable.");
channelId = 0;
}
}
/// <summary>
/// Gets the MTU for a channel. This should take header size into consideration.
/// For example, if MTU is 1200 and a packet header for this channel is 10 in size, this method should return 1190.
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
public override int GetMTU(byte channel)
{
return _unreliableMTU;
}
#endregion
#region Editor.
#if UNITY_EDITOR
private void OnValidate()
{
if (_unreliableMTU < 0)
_unreliableMTU = MINIMUM_UDP_MTU;
else if (_unreliableMTU > MAXIMUM_UDP_MTU)
_unreliableMTU = MAXIMUM_UDP_MTU;
}
#endif
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More