[Add] FishNet
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cd503d67a974984385164c53bd3e518
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Channels.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace FishNet.Transporting
|
||||
{
|
||||
/// <summary>
|
||||
/// States the local connection can be in.
|
||||
/// </summary>
|
||||
[System.Flags]
|
||||
public enum LocalConnectionState : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Connection is fully stopped.
|
||||
/// </summary>
|
||||
Stopped = 1 << 0,
|
||||
/// <summary>
|
||||
/// Connection is stopping.
|
||||
/// </summary>
|
||||
Stopping = 1 << 1,
|
||||
/// <summary>
|
||||
/// Connection is starting but not yet established.
|
||||
/// </summary>
|
||||
Starting = 1 << 2,
|
||||
/// <summary>
|
||||
/// Connection is established.
|
||||
/// </summary>
|
||||
Started = 1 << 3
|
||||
|
||||
// StoppedError = (1 << 4),
|
||||
// StoppedClosed = (1 << 5),
|
||||
}
|
||||
|
||||
public static class LocalConnectionStateExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the connection state is stopped or stopping.
|
||||
/// </summary>
|
||||
public static bool IsStoppedOrStopping(this LocalConnectionState connectionState) => connectionState == LocalConnectionState.Stopped || connectionState == LocalConnectionState.Stopping;
|
||||
|
||||
/// <summary>
|
||||
/// True if the connection state is started or starting.
|
||||
/// </summary>
|
||||
public static bool IsStartedOrStarting(this LocalConnectionState connectionState) => connectionState == LocalConnectionState.Started || connectionState == LocalConnectionState.Starting;
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45640e2b3919981499b359ecc2154d3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/ConnectionStates.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.Transporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Container for connected clients state for a client.
|
||||
/// </summary>
|
||||
public struct ConnectedClientsArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of client ids connected to the server.
|
||||
/// </summary>
|
||||
public List<int> ClientIds { get; private set; }
|
||||
|
||||
public ConnectedClientsArgs(List<int> clientIds)
|
||||
{
|
||||
ClientIds = clientIds;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 718b9d27800e70848b50b2c7b0117e5c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/EventStructures.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,17 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11a2c7610ce4ce34a915683bd4607714
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/IPAddressType.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,32 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d359d6ef33641f41a2ae67d1abdfdd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,35 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fed05d526ab23949bac6cd2bf041c35
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,39 @@
|
||||
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,
|
||||
// Not used with network traffic statistics.
|
||||
Authenticated = 1,
|
||||
// Not used with network traffic statistics.
|
||||
Split = 2,
|
||||
ObjectSpawn = 3,
|
||||
ObjectDespawn = 4,
|
||||
PredictedSpawnResult = 5,
|
||||
SyncType = 7,
|
||||
ServerRpc = 8,
|
||||
ObserversRpc = 9,
|
||||
TargetRpc = 10,
|
||||
// Not used with network traffic statistics.
|
||||
OwnershipChange = 11,
|
||||
Broadcast = 12,
|
||||
// Used only as outbound identifier for network traffic statistics - this Id is never transmitted.
|
||||
BulkSpawnOrDespawn = 13,
|
||||
PingPong = 14,
|
||||
Replicate = 15,
|
||||
Reconcile = 16,
|
||||
// Not used with network traffic statistics.
|
||||
Disconnect = 17,
|
||||
TimingUpdate = 18,
|
||||
UNUSED2 = 19,
|
||||
StateUpdate = 20,
|
||||
// Not used with network traffic statistics.
|
||||
Version = 21
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f3b7256982245b46a2925e2b94ce149
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/PacketId.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,265 @@
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Returns packet loss percentage. Not all transports support this feature.
|
||||
/// </summary>
|
||||
/// <param name = "asServer">True to return packet loss on the server, false to return packet loss on the client.</param>
|
||||
public virtual float GetPacketLoss(bool asServer) => 0f;
|
||||
#endregion
|
||||
|
||||
#region Iterating.
|
||||
/// <summary>
|
||||
/// Processes data received by the socket.
|
||||
/// </summary>
|
||||
/// <param name = "asServer">True to read data from clients, false to read data from the server.
|
||||
public abstract void IterateIncoming(bool asServer);
|
||||
|
||||
/// <summary>
|
||||
/// Processes data to be sent by the socket.
|
||||
/// </summary>
|
||||
/// <param name = "asServer">True to send data from the local server to clients, false to send from the local client to server.
|
||||
public abstract void IterateOutgoing(bool asServer);
|
||||
#endregion
|
||||
|
||||
#region Configuration.
|
||||
/// <summary>
|
||||
/// Returns if the transport is only run locally, offline.
|
||||
/// While true several security checks are disabled.
|
||||
/// </summary>
|
||||
/// <param name = "connectionid">Optional connectionId to check against.</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.";
|
||||
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.";
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78aba14618b37ea4bb067fa95ede84e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transport.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,14 @@
|
||||
using FishNet.Managing;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Transporting
|
||||
{
|
||||
public static class TransportConsts
|
||||
{
|
||||
/// <summary>
|
||||
/// Value used when a transport index is not known or set for a connection.
|
||||
/// </summary>
|
||||
public const int UNSET_TRANSPORT_INDEX = -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 490a5b566793e5a4e8a97a583eab55e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/TransportConsts.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91d2fe16f6fe54e479292316bafa8f87
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3fe0c14d0e92e342a72a5c3c47eded6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,2 @@
|
||||
1.0.0
|
||||
- Initial release.
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b339dd67a0ce7f458236a3ad1d97322
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt
|
||||
uploadId: 866910
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 314b449d3505bd24487ba69b61c2fda5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4f9d944e2ca8484587859cf4ec80b6c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f3b3f854a379684a92d54e1034c3a1a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e13f31983d04611469a690fc65e81b02
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,272 @@
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Logging;
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Layers;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
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>
|
||||
/// Inbound messages which need to be handled.
|
||||
/// </summary>
|
||||
private ConcurrentQueue<Packet> _incoming = new();
|
||||
/// <summary>
|
||||
/// Outbound messages which need to be handled.
|
||||
/// </summary>
|
||||
private Queue<Packet> _outgoing = new();
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// How long in seconds until client times from server.
|
||||
/// </summary>
|
||||
private int _timeout;
|
||||
/// <summary>
|
||||
/// PacketLayer to use with LiteNetLib.
|
||||
/// </summary>
|
||||
private PacketLayerBase _packetLayer;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this for use.
|
||||
/// </summary>
|
||||
internal void Initialize(Transport t, int unreliableMTU, PacketLayerBase packetLayer)
|
||||
{
|
||||
Transport = t;
|
||||
_mtu = unreliableMTU;
|
||||
_packetLayer = packetLayer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Timeout value as seconds.
|
||||
/// </summary>
|
||||
internal void UpdateTimeout(int timeout)
|
||||
{
|
||||
_timeout = timeout;
|
||||
base.UpdateTimeout(NetManager, timeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Polls the socket for new data.
|
||||
/// </summary>
|
||||
internal void PollSocket()
|
||||
{
|
||||
base.PollSocket(NetManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Threaded operation to process client actions.
|
||||
/// </summary>
|
||||
private void ThreadedSocket()
|
||||
{
|
||||
EventBasedNetListener listener = new();
|
||||
listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent;
|
||||
listener.PeerConnectedEvent += Listener_PeerConnectedEvent;
|
||||
listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent;
|
||||
|
||||
NetManager = new(listener, _packetLayer, false);
|
||||
NetManager.DontRoute = ((Tugboat)Transport).DontRoute;
|
||||
NetManager.MtuOverride = _mtu + NetConstants.FragmentedHeaderTotalSize;
|
||||
|
||||
UpdateTimeout(_timeout);
|
||||
|
||||
LocalConnectionStates.Enqueue(LocalConnectionState.Starting);
|
||||
NetManager.Start();
|
||||
NetManager.Connect(_address, _port, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the client connection.
|
||||
/// </summary>
|
||||
internal bool StartConnection(string address, ushort port)
|
||||
{
|
||||
// Force a stop just in case the socket did not clean up.
|
||||
if (GetConnectionState() != LocalConnectionState.Stopped)
|
||||
StopSocket();
|
||||
// Enqueue starting.
|
||||
LocalConnectionStates.Enqueue(LocalConnectionState.Starting);
|
||||
// Iterate to cause state changes to invoke.
|
||||
IterateIncoming();
|
||||
|
||||
// Assign properties.
|
||||
_port = port;
|
||||
_address = address;
|
||||
|
||||
ResetQueues();
|
||||
Task.Run(ThreadedSocket);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the local socket.
|
||||
/// </summary>
|
||||
internal bool StopConnection(DisconnectInfo? info = null)
|
||||
{
|
||||
if (GetConnectionState() == LocalConnectionState.Stopped || GetConnectionState() == LocalConnectionState.Stopping)
|
||||
return false;
|
||||
|
||||
if (info != null)
|
||||
Transport.NetworkManager.Log($"Local client disconnect reason: {info.Value.Reason}.");
|
||||
|
||||
SetConnectionState(LocalConnectionState.Stopping, false);
|
||||
StopSocket();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets queues.
|
||||
/// </summary>
|
||||
private void ResetQueues()
|
||||
{
|
||||
ClearGenericQueue(ref LocalConnectionStates);
|
||||
ClearPacketQueue(ref _incoming);
|
||||
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 (NetManager != null)
|
||||
peer = NetManager.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. */
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
/* 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.TryDequeue(out LocalConnectionState result))
|
||||
SetConnectionState(result, false);
|
||||
|
||||
// Not yet started, cannot continue.
|
||||
LocalConnectionState localState = GetConnectionState();
|
||||
if (localState != LocalConnectionState.Started)
|
||||
{
|
||||
ResetQueues();
|
||||
// If stopped try to kill task.
|
||||
if (localState == LocalConnectionState.Stopped)
|
||||
{
|
||||
StopSocket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Incoming. */
|
||||
while (_incoming.TryDequeue(out Packet incoming))
|
||||
{
|
||||
ClientReceivedDataArgs dataArgs = new(incoming.GetArraySegment(), (Channel)incoming.Channel, Transport.Index);
|
||||
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 (GetConnectionState() != LocalConnectionState.Started)
|
||||
return;
|
||||
|
||||
Send(ref _outgoing, channelId, segment, -1, _mtu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7944de5e4da77594db036e276174ee60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,213 @@
|
||||
using FishNet.Utility.Performance;
|
||||
using LiteNetLib;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FishNet.Transporting.Tugboat
|
||||
{
|
||||
public abstract class CommonSocket
|
||||
{
|
||||
#region Internal.
|
||||
/// <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(connectionState, Transport.Index));
|
||||
else
|
||||
Transport.HandleClientConnectionState(new(connectionState, Transport.Index));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal.
|
||||
/// <summary>
|
||||
/// NetManager for this socket.
|
||||
/// </summary>
|
||||
internal NetManager NetManager;
|
||||
#endregion
|
||||
|
||||
#region Protected.
|
||||
/// <summary>
|
||||
/// Changes to the sockets local connection state.
|
||||
/// </summary>
|
||||
protected ConcurrentQueue<LocalConnectionState> LocalConnectionStates = new();
|
||||
/// <summary>
|
||||
/// Transport controlling this socket.
|
||||
/// </summary>
|
||||
protected Transport Transport;
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Locks the NetManager to stop it.
|
||||
/// </summary>
|
||||
private readonly object _stopLock = new();
|
||||
#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(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 ConcurrentQueue of any type.
|
||||
/// </summary>
|
||||
internal void ClearGenericQueue<T>(ref ConcurrentQueue<T> queue)
|
||||
{
|
||||
while (queue.TryDequeue(out _)) { }
|
||||
}
|
||||
|
||||
/// <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(ConcurrentQueue<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(id, data, dataLen, channel);
|
||||
queue.Enqueue(packet);
|
||||
//Recycle reader.
|
||||
reader.Recycle();
|
||||
}
|
||||
|
||||
internal void PollSocket(NetManager nm)
|
||||
{
|
||||
nm?.PollEvents();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the socket and updates local connection state.
|
||||
/// </summary>
|
||||
protected void StopSocket()
|
||||
{
|
||||
if (NetManager == null)
|
||||
return;
|
||||
|
||||
bool threaded;
|
||||
if (Transport is Tugboat tb)
|
||||
threaded = tb.StopSocketsOnThread;
|
||||
else
|
||||
threaded = false;
|
||||
|
||||
//If using a thread.
|
||||
if (threaded)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
lock (_stopLock)
|
||||
{
|
||||
NetManager?.Stop();
|
||||
NetManager = null;
|
||||
}
|
||||
|
||||
//If not stopped yet also enqueue stop.
|
||||
if (GetConnectionState() != LocalConnectionState.Stopped)
|
||||
LocalConnectionStates.Enqueue(LocalConnectionState.Stopped);
|
||||
});
|
||||
}
|
||||
//Not using a thread.
|
||||
else
|
||||
{
|
||||
NetManager?.Stop();
|
||||
NetManager = null;
|
||||
//If not stopped yet also enqueue stop.
|
||||
if (GetConnectionState() != LocalConnectionState.Stopped)
|
||||
LocalConnectionStates.Enqueue(LocalConnectionState.Stopped);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the port from the socket if active, otherwise returns null.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal ushort? GetPort()
|
||||
{
|
||||
if (NetManager == null || !NetManager.IsRunning)
|
||||
return null;
|
||||
|
||||
int port = NetManager.LocalPort;
|
||||
if (port < 0)
|
||||
port = 0;
|
||||
else if (port > ushort.MaxValue)
|
||||
port = ushort.MaxValue;
|
||||
|
||||
return (ushort)port;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 329169cdf51866c43a8c42e8aeb291fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,502 @@
|
||||
using FishNet.Connection;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Logging;
|
||||
using LiteNetLib;
|
||||
using LiteNetLib.Layers;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
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>
|
||||
/// Inbound messages which need to be handled.
|
||||
/// </summary>
|
||||
private ConcurrentQueue<Packet> _incoming = new();
|
||||
/// <summary>
|
||||
/// Outbound messages which need to be handled.
|
||||
/// </summary>
|
||||
private Queue<Packet> _outgoing = new();
|
||||
/// <summary>
|
||||
/// ConnectionEvents which need to be handled.
|
||||
/// </summary>
|
||||
private ConcurrentQueue<RemoteConnectionEvent> _remoteConnectionEvents = new();
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// How long in seconds until client times from server.
|
||||
/// </summary>
|
||||
private int _timeout;
|
||||
/// <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>
|
||||
/// IPv6 is enabled only on demand, by default LiteNetLib always listens on IPv4 AND IPv6 which causes problems
|
||||
/// if IPv6 is disabled on host. This can be the case in Linux environments
|
||||
/// </summary>
|
||||
private bool _enableIPv6;
|
||||
#endregion
|
||||
|
||||
~ServerSocket()
|
||||
{
|
||||
StopConnection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this for use.
|
||||
/// </summary>
|
||||
/// <param name = "t"></param>
|
||||
internal void Initialize(Transport t, int unreliableMTU, PacketLayerBase packetLayer, bool enableIPv6)
|
||||
{
|
||||
Transport = t;
|
||||
_mtu = unreliableMTU;
|
||||
_packetLayer = packetLayer;
|
||||
_enableIPv6 = enableIPv6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Timeout value as seconds.
|
||||
/// </summary>
|
||||
internal void UpdateTimeout(int timeout)
|
||||
{
|
||||
_timeout = timeout;
|
||||
base.UpdateTimeout(NetManager, timeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Polls the socket for new data.
|
||||
/// </summary>
|
||||
internal void PollSocket()
|
||||
{
|
||||
base.PollSocket(NetManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Threaded operation to process server actions.
|
||||
/// </summary>
|
||||
private void ThreadedSocket()
|
||||
{
|
||||
EventBasedNetListener listener = new();
|
||||
listener.ConnectionRequestEvent += Listener_ConnectionRequestEvent;
|
||||
listener.PeerConnectedEvent += Listener_PeerConnectedEvent;
|
||||
listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent;
|
||||
listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent;
|
||||
|
||||
NetManager = new(listener, _packetLayer, false);
|
||||
NetManager.DontRoute = ((Tugboat)Transport).DontRoute;
|
||||
NetManager.ReuseAddress = ((Tugboat)Transport).ReuseAddress;
|
||||
NetManager.MtuOverride = _mtu + NetConstants.FragmentedHeaderTotalSize;
|
||||
|
||||
UpdateTimeout(_timeout);
|
||||
|
||||
// Set bind addresses.
|
||||
IPAddress ipv4 = null;
|
||||
IPAddress ipv6 = null;
|
||||
|
||||
// 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];
|
||||
Transport.NetworkManager.Log($"IPv4 could not parse correctly but was resolved to {ipv4.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IPAddress.TryParse("0.0.0.0", out ipv4);
|
||||
}
|
||||
|
||||
if (_enableIPv6 && !string.IsNullOrEmpty(_ipv6BindAddress))
|
||||
{
|
||||
// Set ipv6 if protocol is enabled.
|
||||
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 = _enableIPv6 && ipv6 == null ? $"IPv6 address {_ipv6BindAddress} failed to parse. " : string.Empty;
|
||||
if (ipv4FailText != string.Empty || ipv6FailText != string.Empty)
|
||||
{
|
||||
Transport.NetworkManager.Log($"{ipv4FailText}{ipv6FailText}Clear the bind address field to use any bind address.");
|
||||
StopConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
NetManager.IPv6Enabled = _enableIPv6;
|
||||
bool startResult = NetManager.Start(ipv4, ipv6, _port);
|
||||
//If started succcessfully.
|
||||
if (startResult)
|
||||
{
|
||||
LocalConnectionStates.Enqueue(LocalConnectionState.Started);
|
||||
}
|
||||
//Failed to start.
|
||||
else
|
||||
{
|
||||
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>
|
||||
/// 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)
|
||||
{
|
||||
if (GetConnectionState() != LocalConnectionState.Started)
|
||||
{
|
||||
NetworkManager nm = Transport == null ? null : Transport.NetworkManager;
|
||||
string msg = "Server socket is not started.";
|
||||
nm.LogWarning(msg);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
NetPeer peer = GetNetPeer(connectionId, false);
|
||||
if (peer == null)
|
||||
{
|
||||
Transport.NetworkManager.LogWarning($"Connection Id {connectionId} returned a null NetPeer.");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return peer.Address.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a NetPeer for connectionId.
|
||||
/// </summary>
|
||||
/// <param name = "connectionId"></param>
|
||||
/// <returns></returns>
|
||||
private NetPeer GetNetPeer(int connectionId, bool connectedOnly)
|
||||
{
|
||||
if (NetManager != null)
|
||||
{
|
||||
NetPeer peer = NetManager.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)
|
||||
{
|
||||
//Force a stop just in case the socket did not clean up.
|
||||
if (base.GetConnectionState() != LocalConnectionState.Stopped)
|
||||
StopSocket();
|
||||
//Enqueue starting.
|
||||
LocalConnectionStates.Enqueue(LocalConnectionState.Starting);
|
||||
//Iterate to cause state changes to invoke.
|
||||
IterateIncoming();
|
||||
|
||||
//Assign properties.
|
||||
_port = port;
|
||||
_maximumClients = maximumClients;
|
||||
_ipv4BindAddress = ipv4BindAddress;
|
||||
_ipv6BindAddress = ipv6BindAddress;
|
||||
ResetQueues();
|
||||
|
||||
Task.Run(ThreadedSocket);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the local socket.
|
||||
/// </summary>
|
||||
internal bool StopConnection()
|
||||
{
|
||||
if (NetManager == null || base.GetConnectionState() == LocalConnectionState.Stopped || base.GetConnectionState() == LocalConnectionState.Stopping)
|
||||
return false;
|
||||
|
||||
LocalConnectionStates.Enqueue(LocalConnectionState.Stopping);
|
||||
StopSocket();
|
||||
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 (NetManager == 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()
|
||||
{
|
||||
ClearGenericQueue(ref LocalConnectionStates);
|
||||
ClearPacketQueue(ref _incoming);
|
||||
ClearPacketQueue(ref _outgoing);
|
||||
ClearGenericQueue(ref _remoteConnectionEvents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a peer disconnects or times out.
|
||||
/// </summary>
|
||||
private void Listener_PeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo)
|
||||
{
|
||||
_remoteConnectionEvents.Enqueue(new(false, peer.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a peer completes connection.
|
||||
/// </summary>
|
||||
private void Listener_PeerConnectedEvent(NetPeer peer)
|
||||
{
|
||||
_remoteConnectionEvents.Enqueue(new(true, peer.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when data is received from a peer.
|
||||
/// </summary>
|
||||
private void Listener_NetworkReceiveEvent(NetPeer fromPeer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
|
||||
{
|
||||
//If over the MTU.
|
||||
if (reader.AvailableBytes > _mtu)
|
||||
{
|
||||
_remoteConnectionEvents.Enqueue(new(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 (NetManager == null)
|
||||
return;
|
||||
|
||||
//At maximum peers.
|
||||
if (NetManager.ConnectedPeersCount >= _maximumClients)
|
||||
{
|
||||
request.Reject();
|
||||
return;
|
||||
}
|
||||
|
||||
request.AcceptIfKey(key: string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues and processes outgoing.
|
||||
/// </summary>
|
||||
private void DequeueOutgoing()
|
||||
{
|
||||
if (base.GetConnectionState() != LocalConnectionState.Started || NetManager == null)
|
||||
{
|
||||
//Not started, clear outgoing.
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
NetManager.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>
|
||||
internal void IterateIncoming()
|
||||
{
|
||||
/* 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.TryDequeue(out LocalConnectionState result))
|
||||
SetConnectionState(result, true);
|
||||
|
||||
//Not yet started.
|
||||
LocalConnectionState localState = base.GetConnectionState();
|
||||
if (localState != LocalConnectionState.Started)
|
||||
{
|
||||
ResetQueues();
|
||||
//If stopped try to kill task.
|
||||
if (localState == LocalConnectionState.Stopped)
|
||||
{
|
||||
StopSocket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Handle connection and disconnection events.
|
||||
while (_remoteConnectionEvents.TryDequeue(out RemoteConnectionEvent connectionEvent))
|
||||
{
|
||||
RemoteConnectionState state = connectionEvent.Connected ? RemoteConnectionState.Started : RemoteConnectionState.Stopped;
|
||||
Transport.HandleRemoteConnectionState(new(state, connectionEvent.ConnectionId, Transport.Index));
|
||||
}
|
||||
|
||||
//Handle packets.
|
||||
while (_incoming.TryDequeue(out Packet incoming))
|
||||
{
|
||||
//Make sure peer is still connected.
|
||||
NetPeer peer = GetNetPeer(incoming.ConnectionId, true);
|
||||
if (peer != null)
|
||||
{
|
||||
ServerReceivedDataArgs dataArgs = new(incoming.GetArraySegment(), (Channel)incoming.Channel, incoming.ConnectionId, Transport.Index);
|
||||
|
||||
Transport.HandleServerReceivedDataArgs(dataArgs);
|
||||
}
|
||||
|
||||
incoming.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a packet to a single, or all clients.
|
||||
/// </summary>
|
||||
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 Math.Min(_maximumClients, NetworkConnection.MAXIMUM_CLIENTID_WITHOUT_SIMULATED_VALUE);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the MaximumClients value.
|
||||
/// </summary>
|
||||
/// <param name = "value"></param>
|
||||
internal void SetMaximumClients(int value)
|
||||
{
|
||||
_maximumClients = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c6703e8024041e45ae92566123865ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,57 @@
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80c810a1a6a8f3345bb48abfb75c804a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a537e10eb38767b4085a7bec020f9675
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,85 @@
|
||||
#if UNITY_EDITOR
|
||||
using FishNet.Object;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Transporting.Tugboat.Editing
|
||||
{
|
||||
[CustomEditor(typeof(Tugboat), true)]
|
||||
[CanEditMultipleObjects]
|
||||
public class TugboatEditor : Editor
|
||||
{
|
||||
private SerializedProperty _stopSocketsOnThread;
|
||||
private SerializedProperty _dontRoute;
|
||||
private SerializedProperty _reuseAddress;
|
||||
private SerializedProperty _unreliableMtu;
|
||||
private SerializedProperty _ipv4BindAddress;
|
||||
private SerializedProperty _enableIpv6;
|
||||
private SerializedProperty _ipv6BindAddress;
|
||||
private SerializedProperty _port;
|
||||
private SerializedProperty _maximumClients;
|
||||
private SerializedProperty _clientAddress;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
_stopSocketsOnThread = serializedObject.FindProperty(nameof(_stopSocketsOnThread));
|
||||
_dontRoute = serializedObject.FindProperty(nameof(_dontRoute));
|
||||
_reuseAddress = serializedObject.FindProperty(nameof(_reuseAddress));
|
||||
_unreliableMtu = serializedObject.FindProperty(nameof(_unreliableMtu));
|
||||
_ipv4BindAddress = serializedObject.FindProperty(nameof(_ipv4BindAddress));
|
||||
_enableIpv6 = serializedObject.FindProperty(nameof(_enableIpv6));
|
||||
_ipv6BindAddress = serializedObject.FindProperty(nameof(_ipv6BindAddress));
|
||||
_port = serializedObject.FindProperty(nameof(_port));
|
||||
_maximumClients = serializedObject.FindProperty(nameof(_maximumClients));
|
||||
_clientAddress = serializedObject.FindProperty(nameof(_clientAddress));
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
Tugboat tb = (Tugboat)target;
|
||||
|
||||
GUI.enabled = false;
|
||||
EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(tb), typeof(Tugboat), false);
|
||||
GUI.enabled = true;
|
||||
|
||||
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_stopSocketsOnThread);
|
||||
EditorGUILayout.PropertyField(_dontRoute);
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField("Channels", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_unreliableMtu);
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField("Server", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_reuseAddress);
|
||||
EditorGUILayout.PropertyField(_ipv4BindAddress);
|
||||
EditorGUILayout.PropertyField(_enableIpv6);
|
||||
if (_enableIpv6.boolValue == true)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_ipv6BindAddress);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
EditorGUILayout.PropertyField(_port);
|
||||
EditorGUILayout.PropertyField(_maximumClients);
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField("Client", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_clientAddress);
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 578d8bb2922ac0648a90b969512b9779
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12f412d6d520d5c45bb49d177690e507
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace LiteNetLib
|
||||
{
|
||||
internal abstract class BaseChannel
|
||||
{
|
||||
protected readonly NetPeer Peer;
|
||||
protected readonly Queue<NetPacket> OutgoingQueue = new(NetConstants.DefaultWindowSize);
|
||||
private int _isAddedToPeerChannelSendQueue;
|
||||
public int PacketsInQueue => OutgoingQueue.Count;
|
||||
|
||||
protected BaseChannel(NetPeer peer)
|
||||
{
|
||||
Peer = peer;
|
||||
}
|
||||
|
||||
public void AddToQueue(NetPacket packet)
|
||||
{
|
||||
lock (OutgoingQueue)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70b7c357a9f57f5479c5d94550d26280
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs
|
||||
uploadId: 866910
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e609855f95e9034889c882b51aaec68
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs
|
||||
uploadId: 866910
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79436ed5864cf48418ac341ea3c70a6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs
|
||||
uploadId: 866910
|
||||
@@ -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
|
||||
NetDataReader reader = new(null, 0, 0);
|
||||
if (packet.Size > HeaderSize + addrSize)
|
||||
reader.SetSource(packet.RawData, HeaderSize + addrSize, packet.Size);
|
||||
|
||||
return new(connectionTime, packet.ConnectionNumber, peerId, addressBytes, reader);
|
||||
}
|
||||
|
||||
public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectTime, int localId)
|
||||
{
|
||||
// Make initial packet
|
||||
NetPacket packet = new(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(connectionId, connectionNumber, peerId, isReused == 1);
|
||||
}
|
||||
|
||||
public static NetPacket Make(long connectTime, byte connectNum, int localPeerId)
|
||||
{
|
||||
NetPacket packet = new(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)
|
||||
{
|
||||
NetPacket packet = new(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8582459906515843a2f2adb010c3fd7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fa8ca1b7ce0a0041a2ddfebeef8c8fe
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
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 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, 0, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d52aafc1486ec842a8d133ef41dfd39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs
|
||||
uploadId: 866910
|
||||
+17
@@ -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 length);
|
||||
public abstract void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bddd0b5590cce7e42918c72429b84bde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs
|
||||
uploadId: 866910
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
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 length)
|
||||
{
|
||||
if (_byteKey == null)
|
||||
return;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
data[i] = (byte)(data[i] ^ _byteKey[i % _byteKey.Length]);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length)
|
||||
{
|
||||
if (_byteKey == null)
|
||||
return;
|
||||
int cur = offset;
|
||||
for (int i = 0; i < length; i++, cur++)
|
||||
{
|
||||
data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb37499ecdd1ce145b0bcaf4b4f0279e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,66 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>LiteNetLib</AssemblyName>
|
||||
<RootNamespace>LiteNetLib</RootNamespace>
|
||||
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">net6.0;net5.0;netcoreapp3.1;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">net471;net6.0;net5.0;netstandard2.0;netstandard2.1;netcoreapp3.1</TargetFrameworks>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<OutputType>Library</OutputType>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>1701;1702;1705;1591</NoWarn>
|
||||
<PackageVersion>1.2.0</PackageVersion>
|
||||
<Title>Lite reliable UDP library for Mono and .NET </Title>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
|
||||
<AssemblyVersion>1.2.0</AssemblyVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants>$(DefineConstants);LITENETLIB_UNSAFE</DefineConstants>
|
||||
<PackageTags>udp reliable-udp network</PackageTags>
|
||||
<PackageReleaseNotes>https://github.com/RevenantX/LiteNetLib/releases/tag/v1.2.0</PackageReleaseNotes>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/RevenantX/LiteNetLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/RevenantX/LiteNetLib</PackageProjectUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<Version>1.1.0</Version>
|
||||
<Authors>Ruslan Pyrch</Authors>
|
||||
<Copyright>Copyright 2023 Ruslan Pyrch</Copyright>
|
||||
<Description>Lite reliable UDP library for .NET, Mono, and .NET Core</Description>
|
||||
<PackageIcon>LNL.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<TargetFrameworks>net5.0;net6.0;net7.0;net8.0;netstandard2.0;netstandard2.1;net471</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="UpdateUnityDLLS" AfterTargets="CopyFilesToOutputDirectory" Condition=" '$(TargetFramework)' == 'net471' and '$(Configuration)' == 'Release' ">
|
||||
<ItemGroup>
|
||||
<LibraryRelease Include="$(TargetDir)LiteNetLib.dll;$(TargetDir)LiteNetLib.pdb;$(TargetDir)LiteNetLib.xml" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(LibraryRelease)" DestinationFolder="..\LiteNetLibSampleUnity\Assets" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\LNL.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
<None Include="..\README.md">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ec83424c5410b04aab06dddca60e7f6
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,289 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
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
|
||||
{
|
||||
private struct RequestEventData
|
||||
{
|
||||
public IPEndPoint LocalEndPoint;
|
||||
public IPEndPoint RemoteEndPoint;
|
||||
public string Token;
|
||||
}
|
||||
|
||||
private struct SuccessEventData
|
||||
{
|
||||
public IPEndPoint TargetEndPoint;
|
||||
public NatAddressType Type;
|
||||
public string Token;
|
||||
}
|
||||
|
||||
private class NatIntroduceRequestPacket
|
||||
{
|
||||
public IPEndPoint Internal
|
||||
{
|
||||
[Preserve]
|
||||
get;
|
||||
[Preserve]
|
||||
set;
|
||||
}
|
||||
public string Token
|
||||
{
|
||||
[Preserve]
|
||||
get;
|
||||
[Preserve]
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
private class NatIntroduceResponsePacket
|
||||
{
|
||||
public IPEndPoint Internal
|
||||
{
|
||||
[Preserve]
|
||||
get;
|
||||
[Preserve]
|
||||
set;
|
||||
}
|
||||
public IPEndPoint External
|
||||
{
|
||||
[Preserve]
|
||||
get;
|
||||
[Preserve]
|
||||
set;
|
||||
}
|
||||
public string Token
|
||||
{
|
||||
[Preserve]
|
||||
get;
|
||||
[Preserve]
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
private class NatPunchPacket
|
||||
{
|
||||
public string Token
|
||||
{
|
||||
[Preserve]
|
||||
get;
|
||||
[Preserve]
|
||||
set;
|
||||
}
|
||||
public bool IsExternal
|
||||
{
|
||||
[Preserve]
|
||||
get;
|
||||
[Preserve]
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly NetManager _socket;
|
||||
private readonly ConcurrentQueue<RequestEventData> _requestEvents = new();
|
||||
private readonly ConcurrentQueue<SuccessEventData> _successEvents = new();
|
||||
private readonly NetDataReader _cacheReader = new();
|
||||
private readonly NetDataWriter _cacheWriter = new();
|
||||
private readonly NetPacketProcessor _netPacketProcessor = new(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<
|
||||
#if NET5_0_OR_GREATER
|
||||
[DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)]
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
NatIntroduceResponsePacket req = new()
|
||||
{
|
||||
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 SuccessEventData evt))
|
||||
{
|
||||
_natPunchListener.OnNatIntroductionSuccess(evt.TargetEndPoint, evt.Type, evt.Token);
|
||||
}
|
||||
|
||||
while (_requestEvents.TryDequeue(out RequestEventData 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) || masterServerEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
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()
|
||||
{
|
||||
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
|
||||
NatPunchPacket punchPacket = new() { 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 {senderEndPoint} - additional info: {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()
|
||||
{
|
||||
TargetEndPoint = senderEndPoint,
|
||||
Type = req.IsExternal ? NatAddressType.External : NatAddressType.Internal,
|
||||
Token = req.Token
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e608aa88ab820244a90b83eca0f716c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LiteNetLib
|
||||
{
|
||||
internal static class NativeSocket
|
||||
{
|
||||
private static unsafe 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, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize);
|
||||
}
|
||||
|
||||
private static unsafe 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, byte* pinnedBuffer, [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()
|
||||
{
|
||||
{ 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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static unsafe int SendTo(IntPtr socketHandle, byte* pinnedBuffer, 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 SocketError err) ? err : SocketError.SocketError;
|
||||
return (SocketError)error;
|
||||
}
|
||||
|
||||
public static SocketException GetSocketException()
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
if (UnixMode)
|
||||
return NativeErrorToSocketError.TryGetValue(error, out SocketError err) ? new((int)err) : new SocketException((int)SocketError.SocketError);
|
||||
return new(error);
|
||||
}
|
||||
|
||||
public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint)
|
||||
{
|
||||
return UnixMode ? (short)(remoteEndPoint.AddressFamily == AddressFamily.InterNetwork ? AF_INET : AF_INET6) : (short)remoteEndPoint.AddressFamily;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12cfcf4490f2b8f4db979ee833ecf5af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,69 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b42ee5a523e67ff4c9149f91f7fe4245
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,91 @@
|
||||
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();
|
||||
|
||||
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)
|
||||
{
|
||||
WriteLogic(NetLogLevel.Trace, str);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES")]
|
||||
internal static void Write(NetLogLevel level, string str)
|
||||
{
|
||||
WriteLogic(level, str);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES")]
|
||||
[Conditional("DEBUG")]
|
||||
internal static void WriteForce(string str)
|
||||
{
|
||||
WriteLogic(NetLogLevel.Trace, str);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES")]
|
||||
[Conditional("DEBUG")]
|
||||
internal static void WriteForce(NetLogLevel level, string str)
|
||||
{
|
||||
WriteLogic(level, str);
|
||||
}
|
||||
|
||||
internal static void WriteError(string str)
|
||||
{
|
||||
WriteLogic(NetLogLevel.Error, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f50ae51c124bf1439e339eee1fcd6f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs
|
||||
uploadId: 866910
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace LiteNetLib
|
||||
{
|
||||
// minimal hashset class from dotnet with some optimizations
|
||||
public partial class NetManager
|
||||
{
|
||||
private const int MaxPrimeArrayLength = 0x7FFFFFC3;
|
||||
private const int HashPrime = 101;
|
||||
private const int Lower31BitMask = 0x7FFFFFFF;
|
||||
private static readonly int[] Primes =
|
||||
{
|
||||
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
|
||||
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
|
||||
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
|
||||
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
|
||||
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369
|
||||
};
|
||||
|
||||
private static int HashSetGetPrime(int min)
|
||||
{
|
||||
foreach (int prime in Primes)
|
||||
{
|
||||
if (prime >= min)
|
||||
return prime;
|
||||
}
|
||||
|
||||
// Outside of our predefined table. Compute the hard way.
|
||||
for (int i = min | 1; i < int.MaxValue; i += 2)
|
||||
{
|
||||
if (IsPrime(i) && (i - 1) % HashPrime != 0)
|
||||
return i;
|
||||
}
|
||||
return min;
|
||||
|
||||
bool IsPrime(int candidate)
|
||||
{
|
||||
if ((candidate & 1) != 0)
|
||||
{
|
||||
int limit = (int)Math.Sqrt(candidate);
|
||||
for (int divisor = 3; divisor <= limit; divisor += 2)
|
||||
{
|
||||
if (candidate % divisor == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return candidate == 2;
|
||||
}
|
||||
}
|
||||
|
||||
private struct Slot
|
||||
{
|
||||
internal int HashCode;
|
||||
internal int Next;
|
||||
internal NetPeer Value;
|
||||
}
|
||||
|
||||
private int[] _buckets;
|
||||
private Slot[] _slots;
|
||||
private int _count;
|
||||
private int _lastIndex;
|
||||
private int _freeList = -1;
|
||||
private NetPeer[] _peersArray = new NetPeer[32];
|
||||
private readonly ReaderWriterLockSlim _peersLock = new(LockRecursionPolicy.NoRecursion);
|
||||
private volatile NetPeer _headPeer;
|
||||
|
||||
private void ClearPeerSet()
|
||||
{
|
||||
_peersLock.EnterWriteLock();
|
||||
_headPeer = null;
|
||||
if (_lastIndex > 0)
|
||||
{
|
||||
Array.Clear(_slots, 0, _lastIndex);
|
||||
Array.Clear(_buckets, 0, _buckets.Length);
|
||||
_lastIndex = 0;
|
||||
_count = 0;
|
||||
_freeList = -1;
|
||||
}
|
||||
_peersArray = new NetPeer[32];
|
||||
_peersLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
private bool ContainsPeer(NetPeer item)
|
||||
{
|
||||
if (_buckets != null)
|
||||
{
|
||||
int hashCode = item.GetHashCode() & Lower31BitMask;
|
||||
for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next)
|
||||
{
|
||||
if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(item))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets peer by peer id
|
||||
/// </summary>
|
||||
/// <param name = "id">id of peer</param>
|
||||
/// <returns>Peer if peer with id exist, otherwise null</returns>
|
||||
public NetPeer GetPeerById(int id)
|
||||
{
|
||||
return id >= 0 && id < _peersArray.Length ? _peersArray[id] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets peer by peer id
|
||||
/// </summary>
|
||||
/// <param name = "id">id of peer</param>
|
||||
/// <param name = "peer">resulting peer</param>
|
||||
/// <returns>True if peer with id exist, otherwise false</returns>
|
||||
public bool TryGetPeerById(int id, out NetPeer peer)
|
||||
{
|
||||
peer = GetPeerById(id);
|
||||
return peer != null;
|
||||
}
|
||||
|
||||
private void AddPeer(NetPeer peer)
|
||||
{
|
||||
_peersLock.EnterWriteLock();
|
||||
if (_headPeer != null)
|
||||
{
|
||||
peer.NextPeer = _headPeer;
|
||||
_headPeer.PrevPeer = peer;
|
||||
}
|
||||
_headPeer = peer;
|
||||
AddPeerToSet(peer);
|
||||
if (peer.Id >= _peersArray.Length)
|
||||
{
|
||||
int newSize = _peersArray.Length * 2;
|
||||
while (peer.Id >= newSize)
|
||||
newSize *= 2;
|
||||
Array.Resize(ref _peersArray, newSize);
|
||||
}
|
||||
_peersArray[peer.Id] = peer;
|
||||
_peersLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
private void RemovePeer(NetPeer peer)
|
||||
{
|
||||
_peersLock.EnterWriteLock();
|
||||
RemovePeerInternal(peer);
|
||||
_peersLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
private void RemovePeerInternal(NetPeer peer)
|
||||
{
|
||||
if (!RemovePeerFromSet(peer))
|
||||
return;
|
||||
if (peer == _headPeer)
|
||||
_headPeer = peer.NextPeer;
|
||||
|
||||
if (peer.PrevPeer != null)
|
||||
peer.PrevPeer.NextPeer = peer.NextPeer;
|
||||
if (peer.NextPeer != null)
|
||||
peer.NextPeer.PrevPeer = peer.PrevPeer;
|
||||
peer.PrevPeer = null;
|
||||
|
||||
_peersArray[peer.Id] = null;
|
||||
_peerIds.Enqueue(peer.Id);
|
||||
}
|
||||
|
||||
private bool RemovePeerFromSet(NetPeer peer)
|
||||
{
|
||||
if (_buckets == null)
|
||||
return false;
|
||||
if (peer == null)
|
||||
return false;
|
||||
int hashCode = peer.GetHashCode() & Lower31BitMask;
|
||||
int bucket = hashCode % _buckets.Length;
|
||||
int last = -1;
|
||||
for (int i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i].Next)
|
||||
{
|
||||
if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(peer))
|
||||
{
|
||||
if (last < 0)
|
||||
_buckets[bucket] = _slots[i].Next + 1;
|
||||
else
|
||||
_slots[last].Next = _slots[i].Next;
|
||||
_slots[i].HashCode = -1;
|
||||
_slots[i].Value = null;
|
||||
_slots[i].Next = _freeList;
|
||||
|
||||
_count--;
|
||||
if (_count == 0)
|
||||
{
|
||||
_lastIndex = 0;
|
||||
_freeList = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_freeList = i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetPeer(IPEndPoint endPoint, out NetPeer actualValue)
|
||||
{
|
||||
if (_buckets != null)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
// can be NetPeer or IPEndPoint
|
||||
int hashCode = (UseNativeSockets ? endPoint.GetHashCode() : endPoint.Serialize().GetHashCode()) & Lower31BitMask;
|
||||
#else
|
||||
int hashCode = endPoint.GetHashCode() & Lower31BitMask;
|
||||
#endif
|
||||
_peersLock.EnterReadLock();
|
||||
for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next)
|
||||
{
|
||||
if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(endPoint))
|
||||
{
|
||||
actualValue = _slots[i].Value;
|
||||
_peersLock.ExitReadLock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_peersLock.ExitReadLock();
|
||||
}
|
||||
actualValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// only used for NET8
|
||||
private bool TryGetPeer(SocketAddress saddr, out NetPeer actualValue)
|
||||
{
|
||||
if (_buckets != null)
|
||||
{
|
||||
int hashCode = saddr.GetHashCode() & Lower31BitMask;
|
||||
_peersLock.EnterReadLock();
|
||||
for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next)
|
||||
{
|
||||
if (_slots[i].HashCode == hashCode && _slots[i].Value.Serialize().Equals(saddr))
|
||||
{
|
||||
actualValue = _slots[i].Value;
|
||||
_peersLock.ExitReadLock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_peersLock.ExitReadLock();
|
||||
}
|
||||
actualValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool AddPeerToSet(NetPeer value)
|
||||
{
|
||||
if (_buckets == null)
|
||||
{
|
||||
int size = HashSetGetPrime(0);
|
||||
_buckets = new int[size];
|
||||
_slots = new Slot[size];
|
||||
}
|
||||
|
||||
int hashCode = value.GetHashCode() & Lower31BitMask;
|
||||
int bucket = hashCode % _buckets.Length;
|
||||
for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next)
|
||||
{
|
||||
if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(value))
|
||||
return false;
|
||||
}
|
||||
|
||||
int index;
|
||||
if (_freeList >= 0)
|
||||
{
|
||||
index = _freeList;
|
||||
_freeList = _slots[index].Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_lastIndex == _slots.Length)
|
||||
{
|
||||
// increase capacity
|
||||
int newSize = 2 * _count;
|
||||
newSize = (uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > _count ? MaxPrimeArrayLength : HashSetGetPrime(newSize);
|
||||
|
||||
// Able to increase capacity; copy elements to larger array and rehash
|
||||
Slot[] newSlots = new Slot[newSize];
|
||||
Array.Copy(_slots, 0, newSlots, 0, _lastIndex);
|
||||
_buckets = new int[newSize];
|
||||
for (int i = 0; i < _lastIndex; i++)
|
||||
{
|
||||
int b = newSlots[i].HashCode % newSize;
|
||||
newSlots[i].Next = _buckets[b] - 1;
|
||||
_buckets[b] = i + 1;
|
||||
}
|
||||
_slots = newSlots;
|
||||
// this will change during resize
|
||||
bucket = hashCode % _buckets.Length;
|
||||
}
|
||||
index = _lastIndex;
|
||||
_lastIndex++;
|
||||
}
|
||||
_slots[index].HashCode = hashCode;
|
||||
_slots[index].Value = value;
|
||||
_slots[index].Next = _buckets[bucket] - 1;
|
||||
_buckets[bucket] = index + 1;
|
||||
_count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3dad2c47fd51d14fbb281bf3cb231e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs
|
||||
uploadId: 866910
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
|
||||
namespace LiteNetLib
|
||||
{
|
||||
public partial class NetManager
|
||||
{
|
||||
private NetPacket _poolHead;
|
||||
private readonly object _poolLock = new();
|
||||
/// <summary>
|
||||
/// Maximum packet pool size (increase if you have tons of packets sending)
|
||||
/// </summary>
|
||||
public int PacketPoolSize = 1000;
|
||||
public int PoolCount { get; private set; }
|
||||
|
||||
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)
|
||||
{
|
||||
if (size > NetConstants.MaxPacketSize)
|
||||
return new(size);
|
||||
|
||||
NetPacket packet;
|
||||
lock (_poolLock)
|
||||
{
|
||||
packet = _poolHead;
|
||||
if (packet == null)
|
||||
return new(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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d384bada29340e542945f001cad348ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs
|
||||
uploadId: 866910
|
||||
+682
@@ -0,0 +1,682 @@
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
#define UNITY_SOCKET_FIX
|
||||
#endif
|
||||
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
|
||||
{
|
||||
private const int ReceivePollingTime = 500000; // 0.5 second
|
||||
private Socket _udpSocketv4;
|
||||
private Socket _udpSocketv6;
|
||||
private Thread _receiveThread;
|
||||
private IPEndPoint _bufferEndPointv4;
|
||||
private IPEndPoint _bufferEndPointv6;
|
||||
#if UNITY_SOCKET_FIX
|
||||
private PausedSocketFix _pausedSocketFix;
|
||||
private bool _useSocketFix;
|
||||
#endif
|
||||
#if NET8_0_OR_GREATER
|
||||
private readonly SocketAddress _sockAddrCacheV4 = new SocketAddress(AddressFamily.InterNetwork);
|
||||
private readonly SocketAddress _sockAddrCacheV6 = new SocketAddress(AddressFamily.InterNetworkV6);
|
||||
#endif
|
||||
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;
|
||||
|
||||
// special case in iOS (and possibly android that should be resolved in unity)
|
||||
internal bool NotConnected;
|
||||
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 ProcessError(SocketException ex)
|
||||
{
|
||||
switch (ex.SocketErrorCode)
|
||||
{
|
||||
case SocketError.NotConnected:
|
||||
NotConnected = true;
|
||||
return true;
|
||||
case SocketError.Interrupted:
|
||||
case SocketError.NotSocket:
|
||||
case SocketError.OperationAborted:
|
||||
return true;
|
||||
case SocketError.ConnectionReset:
|
||||
case SocketError.MessageSize:
|
||||
case SocketError.TimedOut:
|
||||
case SocketError.NetworkReset:
|
||||
case SocketError.WouldBlock:
|
||||
// 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, int maxReceive)
|
||||
{
|
||||
// Reading data
|
||||
try
|
||||
{
|
||||
int packetsReceived = 0;
|
||||
while (socket.Available > 0)
|
||||
{
|
||||
ReceiveFrom(socket, ref bufferEndPoint);
|
||||
packetsReceived++;
|
||||
if (packetsReceived == maxReceive)
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
ProcessError(ex);
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
catch (Exception e)
|
||||
{
|
||||
// protects socket receive thread
|
||||
NetDebug.WriteError("[NM] SocketReceiveThread error: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private void NativeReceiveLogic()
|
||||
{
|
||||
IntPtr socketHandle4 = _udpSocketv4.Handle;
|
||||
IntPtr socketHandle6 = _udpSocketv6?.Handle ?? IntPtr.Zero;
|
||||
byte[] addrBuffer4 = new byte[NativeSocket.IPv4AddrSize];
|
||||
byte[] addrBuffer6 = new byte[NativeSocket.IPv6AddrSize];
|
||||
IPEndPoint tempEndPoint = new(IPAddress.Any, 0);
|
||||
List<Socket> selectReadList = new(2);
|
||||
Socket socketv4 = _udpSocketv4;
|
||||
Socket socketV6 = _udpSocketv6;
|
||||
NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize);
|
||||
|
||||
while (IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (socketV6 == null)
|
||||
{
|
||||
if (NativeReceiveFrom(socketHandle4, addrBuffer4) == false)
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
bool messageReceived = false;
|
||||
if (socketv4.Available != 0 || selectReadList.Contains(socketv4))
|
||||
{
|
||||
if (NativeReceiveFrom(socketHandle4, addrBuffer4) == false)
|
||||
return;
|
||||
messageReceived = true;
|
||||
}
|
||||
if (socketV6.Available != 0 || selectReadList.Contains(socketV6))
|
||||
{
|
||||
if (NativeReceiveFrom(socketHandle6, addrBuffer6) == false)
|
||||
return;
|
||||
messageReceived = true;
|
||||
}
|
||||
|
||||
selectReadList.Clear();
|
||||
|
||||
if (messageReceived)
|
||||
continue;
|
||||
|
||||
selectReadList.Add(socketv4);
|
||||
selectReadList.Add(socketV6);
|
||||
|
||||
Socket.Select(selectReadList, null, null, ReceivePollingTime);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeReceiveFrom(IntPtr s, byte[] address)
|
||||
{
|
||||
int addrSize = address.Length;
|
||||
packet.Size = NativeSocket.RecvFrom(s, packet.RawData, NetConstants.MaxPacketSize, address, ref addrSize);
|
||||
if (packet.Size == 0)
|
||||
return true; // socket closed or empty packet
|
||||
|
||||
if (packet.Size == -1)
|
||||
{
|
||||
// Linux timeout EAGAIN
|
||||
return ProcessError(new((int)NativeSocket.GetSocketError())) == false;
|
||||
}
|
||||
|
||||
// NetDebug.WriteForce($"[R]Received data from {endPoint}, result: {packet.Size}");
|
||||
// refresh temp Addr/Port
|
||||
short family = (short)((address[1] << 8) | address[0]);
|
||||
tempEndPoint.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
|
||||
tempEndPoint.Address = new(new ReadOnlySpan<byte>(address, 8, 16), scope);
|
||||
#else
|
||||
byte[] addrBuffer = new byte[16];
|
||||
Buffer.BlockCopy(address, 8, addrBuffer, 0, 16);
|
||||
tempEndPoint.Address = new(addrBuffer, scope);
|
||||
#endif
|
||||
}
|
||||
else // IPv4
|
||||
{
|
||||
long ipv4Addr = unchecked((uint)((address[4] & 0x000000FF) | ((address[5] << 8) & 0x0000FF00) | ((address[6] << 16) & 0x00FF0000) | (address[7] << 24)));
|
||||
tempEndPoint.Address = new(ipv4Addr);
|
||||
}
|
||||
|
||||
if (TryGetPeer(tempEndPoint, out NetPeer peer))
|
||||
{
|
||||
// use cached native ep
|
||||
OnMessageReceived(packet, peer);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnMessageReceived(packet, tempEndPoint);
|
||||
tempEndPoint = new(IPAddress.Any, 0);
|
||||
}
|
||||
packet = PoolGetPacket(NetConstants.MaxPacketSize);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReceiveFrom(Socket s, ref EndPoint bufferEndPoint)
|
||||
{
|
||||
NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize);
|
||||
#if NET8_0_OR_GREATER
|
||||
var sockAddr = s.AddressFamily == AddressFamily.InterNetwork ? _sockAddrCacheV4 : _sockAddrCacheV6;
|
||||
packet.Size = s.ReceiveFrom(packet, SocketFlags.None, sockAddr);
|
||||
OnMessageReceived(packet, TryGetPeer(sockAddr, out var peer) ? peer : (IPEndPoint)bufferEndPoint.Create(sockAddr));
|
||||
#else
|
||||
packet.Size = s.ReceiveFrom(packet.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, ref bufferEndPoint);
|
||||
OnMessageReceived(packet, (IPEndPoint)bufferEndPoint);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ReceiveLogic()
|
||||
{
|
||||
EndPoint bufferEndPoint4 = new IPEndPoint(IPAddress.Any, 0);
|
||||
EndPoint bufferEndPoint6 = new IPEndPoint(IPAddress.IPv6Any, 0);
|
||||
List<Socket> selectReadList = new(2);
|
||||
Socket socketv4 = _udpSocketv4;
|
||||
Socket socketV6 = _udpSocketv6;
|
||||
|
||||
while (IsRunning)
|
||||
{
|
||||
// Reading data
|
||||
try
|
||||
{
|
||||
if (socketV6 == null)
|
||||
{
|
||||
if (socketv4.Available == 0 && !socketv4.Poll(ReceivePollingTime, SelectMode.SelectRead))
|
||||
continue;
|
||||
ReceiveFrom(socketv4, ref bufferEndPoint4);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool messageReceived = false;
|
||||
if (socketv4.Available != 0 || selectReadList.Contains(socketv4))
|
||||
{
|
||||
ReceiveFrom(socketv4, ref bufferEndPoint4);
|
||||
messageReceived = true;
|
||||
}
|
||||
if (socketV6.Available != 0 || selectReadList.Contains(socketV6))
|
||||
{
|
||||
ReceiveFrom(socketV6, ref bufferEndPoint6);
|
||||
messageReceived = true;
|
||||
}
|
||||
|
||||
selectReadList.Clear();
|
||||
|
||||
if (messageReceived)
|
||||
continue;
|
||||
|
||||
selectReadList.Add(socketv4);
|
||||
selectReadList.Add(socketV6);
|
||||
Socket.Select(selectReadList, null, null, ReceivePollingTime);
|
||||
}
|
||||
// NetDebug.Write(NetLogLevel.Trace, $"[R]Received data from {bufferEndPoint}, result: {packet.Size}");
|
||||
}
|
||||
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 && NotConnected == false)
|
||||
return false;
|
||||
|
||||
NotConnected = false;
|
||||
_manualMode = manualMode;
|
||||
UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported;
|
||||
_udpSocketv4 = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
if (!BindSocket(_udpSocketv4, new(addressIPv4, port)))
|
||||
return false;
|
||||
|
||||
LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port;
|
||||
|
||||
#if UNITY_SOCKET_FIX
|
||||
if (_useSocketFix && _pausedSocketFix == null)
|
||||
_pausedSocketFix = new(this, addressIPv4, addressIPv6, port, manualMode);
|
||||
#endif
|
||||
|
||||
IsRunning = true;
|
||||
if (_manualMode)
|
||||
{
|
||||
_bufferEndPointv4 = new(IPAddress.Any, 0);
|
||||
}
|
||||
|
||||
// Check IPv6 support
|
||||
if (IPv6Support && IPv6Enabled)
|
||||
{
|
||||
_udpSocketv6 = new(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
// Use one port for two sockets
|
||||
if (BindSocket(_udpSocketv6, new(addressIPv6, LocalPort)))
|
||||
{
|
||||
if (_manualMode)
|
||||
_bufferEndPointv6 = new(IPAddress.IPv6Any, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_udpSocketv6 = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!manualMode)
|
||||
{
|
||||
ThreadStart ts = ReceiveLogic;
|
||||
if (UseNativeSockets)
|
||||
ts = NativeReceiveLogic;
|
||||
_receiveThread = new(ts)
|
||||
{
|
||||
Name = $"ReceiveThread({LocalPort})",
|
||||
IsBackground = true
|
||||
};
|
||||
_receiveThread.Start();
|
||||
if (_logicThread == null)
|
||||
{
|
||||
_logicThread = new(UpdateLogic) { Name = "LogicThread", IsBackground = true };
|
||||
_logicThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
socket.Blocking = true;
|
||||
|
||||
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);
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, DontRoute);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it
|
||||
}
|
||||
if (ep.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
Ttl = NetConstants.SocketTTL;
|
||||
|
||||
try
|
||||
{
|
||||
socket.EnableBroadcast = true;
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
NetDebug.WriteError($"[B]Broadcast error: {e.SocketErrorCode}");
|
||||
}
|
||||
|
||||
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_SOCKET_FIX
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Socket socket = _udpSocketv4;
|
||||
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support)
|
||||
{
|
||||
socket = _udpSocketv6;
|
||||
if (socket == null)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result;
|
||||
try
|
||||
{
|
||||
if (UseNativeSockets && remoteEndPoint is NetPeer peer)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* dataWithOffset = &message[start])
|
||||
{
|
||||
result = NativeSocket.SendTo(socket.Handle, dataWithOffset, length, peer.NativeAddress, peer.NativeAddress.Length);
|
||||
}
|
||||
}
|
||||
if (result == -1)
|
||||
throw NativeSocket.GetSocketException();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
result = socket.SendTo(new ReadOnlySpan<byte>(message, start, length), SocketFlags.None, remoteEndPoint.Serialize());
|
||||
#else
|
||||
result = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint);
|
||||
#endif
|
||||
}
|
||||
// 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: {length}");
|
||||
return 0;
|
||||
|
||||
case SocketError.HostUnreachable:
|
||||
case SocketError.NetworkUnreachable:
|
||||
if (DisconnectOnUnreachable && remoteEndPoint is NetPeer peer)
|
||||
{
|
||||
DisconnectPeerForce(peer, ex.SocketErrorCode == SocketError.HostUnreachable ? DisconnectReason.HostUnreachable : DisconnectReason.NetworkUnreachable, ex.SocketErrorCode, null);
|
||||
}
|
||||
|
||||
CreateEvent(NetEvent.EType.Error, remoteEndPoint: remoteEndPoint, errorCode: ex.SocketErrorCode);
|
||||
return -1;
|
||||
|
||||
case SocketError.Shutdown:
|
||||
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 (!IsRunning)
|
||||
return false;
|
||||
|
||||
NetPacket packet;
|
||||
if (_extraPacketLayer != null)
|
||||
{
|
||||
int headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast);
|
||||
packet = PoolGetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer);
|
||||
packet.Property = PacketProperty.Broadcast;
|
||||
Buffer.BlockCopy(data, start, packet.RawData, headerSize, length);
|
||||
int 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 (SocketException ex)
|
||||
{
|
||||
if (ex.SocketErrorCode == SocketError.HostUnreachable)
|
||||
return broadcastSuccess;
|
||||
NetDebug.WriteError($"[S][MCAST] {ex}");
|
||||
return broadcastSuccess;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetDebug.WriteError($"[S][MCAST] {ex}");
|
||||
return broadcastSuccess;
|
||||
}
|
||||
finally
|
||||
{
|
||||
PoolRecycle(packet);
|
||||
}
|
||||
|
||||
return broadcastSuccess || multicastSuccess;
|
||||
}
|
||||
|
||||
private void CloseSocket()
|
||||
{
|
||||
IsRunning = false;
|
||||
_udpSocketv4?.Close();
|
||||
_udpSocketv6?.Close();
|
||||
_udpSocketv4 = null;
|
||||
_udpSocketv6 = null;
|
||||
if (_receiveThread != null && _receiveThread != Thread.CurrentThread)
|
||||
_receiveThread.Join();
|
||||
_receiveThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cc0423407df3e74a8e5a15dbbad2598
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs
|
||||
uploadId: 866910
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37d95580df7122c44b9333cd3ab77732
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
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);
|
||||
}
|
||||
|
||||
#if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1
|
||||
public static implicit operator Span<byte>(NetPacket p) => new(p.RawData, 0, p.Size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7ddd322169be074f870f73db1a55255
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs
|
||||
uploadId: 866910
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e1a9277334c51545b92369c8e4c6d74
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,71 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15d4b62077bda58428b77d57fa4a9288
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,221 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
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
|
||||
{
|
||||
private static readonly NetworkSorter NetworkSorter = new();
|
||||
|
||||
public static IPEndPoint MakeEndPoint(string hostStr, int port)
|
||||
{
|
||||
return new(ResolveAddress(hostStr), port);
|
||||
}
|
||||
|
||||
public static IPAddress ResolveAddress(string hostStr)
|
||||
{
|
||||
if (hostStr == "localhost")
|
||||
return IPAddress.Loopback;
|
||||
|
||||
if (!IPAddress.TryParse(hostStr, out IPAddress 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();
|
||||
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
|
||||
{
|
||||
// Sort networks interfaces so it prefer Wifi over Cellular networks
|
||||
// Most cellulars networks seems to be incompatible with NAT Punch
|
||||
NetworkInterface[] networks = NetworkInterface.GetAllNetworkInterfaces();
|
||||
Array.Sort(networks, NetworkSorter);
|
||||
|
||||
foreach (NetworkInterface ni in networks)
|
||||
{
|
||||
// Skip loopback and disabled network interfaces
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
|
||||
IPInterfaceProperties ipProps = ni.GetIPProperties();
|
||||
|
||||
// Skip address without gateway
|
||||
if (ipProps.GatewayAddresses.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (UnicastIPAddressInformation ip in ipProps.UnicastAddresses)
|
||||
{
|
||||
IPAddress 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();
|
||||
|
||||
/// <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: {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: {ni.Name}, Type: {ni.NetworkInterfaceType}, Ip: {ip.Address}, OpStatus: {ni.OperationalStatus}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
NetDebug.WriteForce(NetLogLevel.Info, $"Error while getting interface infos: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the most obvious choice for the local IP
|
||||
// Ethernet > Wifi > Others > Cellular
|
||||
internal class NetworkSorter : IComparer<NetworkInterface>
|
||||
{
|
||||
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
|
||||
public int Compare(NetworkInterface a, NetworkInterface b)
|
||||
{
|
||||
bool isCellularA = a.NetworkInterfaceType == NetworkInterfaceType.Wman || a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2;
|
||||
|
||||
bool isCellularB = b.NetworkInterfaceType == NetworkInterfaceType.Wman || b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2;
|
||||
|
||||
bool isWifiA = a.NetworkInterfaceType == NetworkInterfaceType.Wireless80211;
|
||||
bool isWifiB = b.NetworkInterfaceType == NetworkInterfaceType.Wireless80211;
|
||||
|
||||
bool isEthernetA = a.NetworkInterfaceType == NetworkInterfaceType.Ethernet || a.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || a.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT;
|
||||
|
||||
bool isEthernetB = b.NetworkInterfaceType == NetworkInterfaceType.Ethernet || b.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || b.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT;
|
||||
|
||||
bool isOtherA = !isCellularA && !isWifiA && !isEthernetA;
|
||||
bool isOtherB = !isCellularB && !isWifiB && !isEthernetB;
|
||||
|
||||
int priorityA = isEthernetA ? 3 : isWifiA ? 2 : isOtherA ? 1 : 0;
|
||||
int priorityB = isEthernetB ? 3 : isWifiB ? 2 : isOtherB ? 1 : 0;
|
||||
|
||||
return priorityA > priorityB ? -1 : priorityA < priorityB ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab6438405a73f8c46ac22cd2f259a0e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,57 @@
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
using System.Net;
|
||||
|
||||
namespace LiteNetLib
|
||||
{
|
||||
public class PausedSocketFix
|
||||
{
|
||||
private readonly NetManager _netManager;
|
||||
private readonly IPAddress _ipv4;
|
||||
private readonly IPAddress _ipv6;
|
||||
private readonly int _port;
|
||||
private readonly bool _manualMode;
|
||||
private bool _initialized;
|
||||
|
||||
public PausedSocketFix(NetManager netManager, IPAddress ipv4, IPAddress ipv6, int port, bool manualMode)
|
||||
{
|
||||
_netManager = netManager;
|
||||
_ipv4 = ipv4;
|
||||
_ipv6 = ipv6;
|
||||
_port = port;
|
||||
_manualMode = manualMode;
|
||||
UnityEngine.Application.focusChanged += Application_focusChanged;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
public void Deinitialize()
|
||||
{
|
||||
if (_initialized)
|
||||
UnityEngine.Application.focusChanged -= Application_focusChanged;
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
private void Application_focusChanged(bool focused)
|
||||
{
|
||||
// If coming back into focus see if a reconnect is needed.
|
||||
if (focused)
|
||||
{
|
||||
// try reconnect
|
||||
if (!_initialized)
|
||||
return;
|
||||
// Was intentionally disconnected at some point.
|
||||
if (!_netManager.IsRunning)
|
||||
return;
|
||||
// Socket is in working state.
|
||||
if (_netManager.NotConnected == false)
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85ea50adb87db074c9a1c587f35aebb2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76e47a542bb12e043b874c7180d98319
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,334 @@
|
||||
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: {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();
|
||||
|
||||
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(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: {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: {pendingSeq} - true");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
lock (OutgoingQueue)
|
||||
{
|
||||
while (OutgoingQueue.Count > 0)
|
||||
{
|
||||
int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart);
|
||||
if (relate >= _windowSize)
|
||||
break;
|
||||
|
||||
NetPacket netPacket = OutgoingQueue.Dequeue();
|
||||
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.Count > 0;
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7e20c169333af54fa233b0d70b19a61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs
|
||||
uploadId: 866910
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
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(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)
|
||||
{
|
||||
NetPacket packet = _lastPacket;
|
||||
if (packet != null)
|
||||
{
|
||||
_lastPacketSendTime = currentTime;
|
||||
Peer.SendUserData(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (OutgoingQueue)
|
||||
{
|
||||
while (OutgoingQueue.Count > 0)
|
||||
{
|
||||
NetPacket packet = OutgoingQueue.Dequeue();
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4e1f97908b70e642baea0fc22724282
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,12 @@
|
||||
#if NET5_0_OR_GREATER
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes;
|
||||
|
||||
namespace LiteNetLib
|
||||
{
|
||||
internal static class Trimming
|
||||
{
|
||||
internal const DynamicallyAccessedMemberTypes SerializerMemberTypes = PublicProperties | NonPublicProperties;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af5a9e06ff14c6c42ab091c2c19e2028
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ebeb715b0d0f844bad181703daa09db
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,138 @@
|
||||
#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)
|
||||
{
|
||||
uint 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]];
|
||||
|
||||
uint 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]];
|
||||
|
||||
uint 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]];
|
||||
|
||||
uint 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 912ba506b0945b743be5c4129177024c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs
|
||||
uploadId: 866910
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LiteNetLib.Utils
|
||||
{
|
||||
public static class FastBitConverter
|
||||
{
|
||||
#if (LITENETLIB_UNSAFE || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER) && !BIGENDIAN
|
||||
#if LITENETLIB_UNSAFE
|
||||
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 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
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, double value)
|
||||
{
|
||||
ConverterHelperDouble ch = new() { Adouble = value };
|
||||
WriteLittleEndian(bytes, startIndex, ch.Along);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, float value)
|
||||
{
|
||||
ConverterHelperFloat ch = new() { Afloat = value };
|
||||
WriteLittleEndian(bytes, startIndex, ch.Aint);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, short value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, ushort value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (short)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, int value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, uint value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (int)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, long value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (ulong)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, ulong value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21018f84ccfb8244e99b5ec22ceb91c9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs
|
||||
uploadId: 866910
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace LiteNetLib.Utils
|
||||
{
|
||||
public interface INetSerializable
|
||||
{
|
||||
void Serialize(NetDataWriter writer);
|
||||
void Deserialize(NetDataReader reader);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c26b17a74d3fb8b498fc89d253f1742a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs
|
||||
uploadId: 866910
|
||||
+761
@@ -0,0 +1,761 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LiteNetLib.Utils
|
||||
{
|
||||
public class NetDataReader
|
||||
{
|
||||
protected byte[] _data;
|
||||
protected int _position;
|
||||
protected int _dataSize;
|
||||
public byte[] RawData
|
||||
{
|
||||
get => _data;
|
||||
}
|
||||
public int RawDataSize
|
||||
{
|
||||
get => _dataSize;
|
||||
}
|
||||
public int UserDataOffset { get; private set; }
|
||||
public int UserDataSize
|
||||
{
|
||||
get => _dataSize - UserDataOffset;
|
||||
}
|
||||
public bool IsNull
|
||||
{
|
||||
get => _data == null;
|
||||
}
|
||||
public int Position
|
||||
{
|
||||
get => _position;
|
||||
}
|
||||
public bool EndOfData
|
||||
{
|
||||
get => _position == _dataSize;
|
||||
}
|
||||
public int AvailableBytes
|
||||
{
|
||||
get => _dataSize - _position;
|
||||
}
|
||||
|
||||
public void SkipBytes(int count)
|
||||
{
|
||||
_position += count;
|
||||
}
|
||||
|
||||
public void SetPosition(int position)
|
||||
{
|
||||
_position = position;
|
||||
}
|
||||
|
||||
public void SetSource(NetDataWriter dataWriter)
|
||||
{
|
||||
_data = dataWriter.Data;
|
||||
_position = 0;
|
||||
UserDataOffset = 0;
|
||||
_dataSize = dataWriter.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source)
|
||||
{
|
||||
_data = source;
|
||||
_position = 0;
|
||||
UserDataOffset = 0;
|
||||
_dataSize = source.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source, int offset, int maxSize)
|
||||
{
|
||||
_data = source;
|
||||
_position = offset;
|
||||
UserDataOffset = 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 void Get<T>(out T result) where T : struct, INetSerializable
|
||||
{
|
||||
result = default;
|
||||
result.Deserialize(this);
|
||||
}
|
||||
|
||||
public void Get<T>(out T result, Func<T> constructor) where T : class, INetSerializable
|
||||
{
|
||||
result = constructor();
|
||||
result.Deserialize(this);
|
||||
}
|
||||
|
||||
public void Get(out IPEndPoint result)
|
||||
{
|
||||
result = GetNetEndPoint();
|
||||
}
|
||||
|
||||
public void Get(out byte result)
|
||||
{
|
||||
result = GetByte();
|
||||
}
|
||||
|
||||
public void Get(out sbyte result)
|
||||
{
|
||||
result = (sbyte)GetByte();
|
||||
}
|
||||
|
||||
public void Get(out bool result)
|
||||
{
|
||||
result = GetBool();
|
||||
}
|
||||
|
||||
public void Get(out char result)
|
||||
{
|
||||
result = GetChar();
|
||||
}
|
||||
|
||||
public void Get(out ushort result)
|
||||
{
|
||||
result = GetUShort();
|
||||
}
|
||||
|
||||
public void Get(out short result)
|
||||
{
|
||||
result = GetShort();
|
||||
}
|
||||
|
||||
public void Get(out ulong result)
|
||||
{
|
||||
result = GetULong();
|
||||
}
|
||||
|
||||
public void Get(out long result)
|
||||
{
|
||||
result = GetLong();
|
||||
}
|
||||
|
||||
public void Get(out uint result)
|
||||
{
|
||||
result = GetUInt();
|
||||
}
|
||||
|
||||
public void Get(out int result)
|
||||
{
|
||||
result = GetInt();
|
||||
}
|
||||
|
||||
public void Get(out double result)
|
||||
{
|
||||
result = GetDouble();
|
||||
}
|
||||
|
||||
public void Get(out float result)
|
||||
{
|
||||
result = GetFloat();
|
||||
}
|
||||
|
||||
public void Get(out string result)
|
||||
{
|
||||
result = GetString();
|
||||
}
|
||||
|
||||
public void Get(out string result, int maxLength)
|
||||
{
|
||||
result = GetString(maxLength);
|
||||
}
|
||||
|
||||
public IPEndPoint GetNetEndPoint()
|
||||
{
|
||||
string host = GetString(1000);
|
||||
int port = GetInt();
|
||||
return NetUtils.MakeEndPoint(host, port);
|
||||
}
|
||||
|
||||
public byte GetByte()
|
||||
{
|
||||
byte res = _data[_position];
|
||||
_position++;
|
||||
return res;
|
||||
}
|
||||
|
||||
public sbyte GetSByte()
|
||||
{
|
||||
return (sbyte)GetByte();
|
||||
}
|
||||
|
||||
public T[] GetArray<T>(ushort size)
|
||||
{
|
||||
ushort length = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
T[] result = new T[length];
|
||||
length *= size;
|
||||
Buffer.BlockCopy(_data, _position, result, 0, length);
|
||||
_position += length;
|
||||
return result;
|
||||
}
|
||||
|
||||
public T[] GetArray<T>() where T : INetSerializable, new()
|
||||
{
|
||||
ushort length = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
T[] result = new T[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
T item = new();
|
||||
item.Deserialize(this);
|
||||
result[i] = item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool[] GetBoolArray()
|
||||
{
|
||||
return GetArray<bool>(1);
|
||||
}
|
||||
|
||||
public ushort[] GetUShortArray()
|
||||
{
|
||||
return GetArray<ushort>(2);
|
||||
}
|
||||
|
||||
public short[] GetShortArray()
|
||||
{
|
||||
return GetArray<short>(2);
|
||||
}
|
||||
|
||||
public int[] GetIntArray()
|
||||
{
|
||||
return GetArray<int>(4);
|
||||
}
|
||||
|
||||
public uint[] GetUIntArray()
|
||||
{
|
||||
return GetArray<uint>(4);
|
||||
}
|
||||
|
||||
public float[] GetFloatArray()
|
||||
{
|
||||
return GetArray<float>(4);
|
||||
}
|
||||
|
||||
public double[] GetDoubleArray()
|
||||
{
|
||||
return GetArray<double>(8);
|
||||
}
|
||||
|
||||
public long[] GetLongArray()
|
||||
{
|
||||
return GetArray<long>(8);
|
||||
}
|
||||
|
||||
public ulong[] GetULongArray()
|
||||
{
|
||||
return GetArray<ulong>(8);
|
||||
}
|
||||
|
||||
public string[] GetStringArray()
|
||||
{
|
||||
ushort length = GetUShort();
|
||||
string[] arr = new string[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
arr[i] = GetString();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Note that "maxStringLength" only limits the number of characters in a string, not its size in bytes.
|
||||
/// Strings that exceed this parameter are returned as empty
|
||||
/// </summary>
|
||||
public string[] GetStringArray(int maxStringLength)
|
||||
{
|
||||
ushort length = GetUShort();
|
||||
string[] arr = new string[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
arr[i] = GetString(maxStringLength);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public bool GetBool()
|
||||
{
|
||||
return GetByte() == 1;
|
||||
}
|
||||
|
||||
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 string.Empty;
|
||||
}
|
||||
|
||||
int actualSize = size - 1;
|
||||
if (actualSize >= NetDataWriter.StringBufferMaxLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ArraySegment<byte> data = GetBytesSegment(actualSize);
|
||||
|
||||
return maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(data.Array, data.Offset, data.Count) > maxLength ? string.Empty : NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count);
|
||||
}
|
||||
|
||||
public string GetString()
|
||||
{
|
||||
ushort size = GetUShort();
|
||||
if (size == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
int actualSize = size - 1;
|
||||
if (actualSize >= NetDataWriter.StringBufferMaxLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ArraySegment<byte> data = GetBytesSegment(actualSize);
|
||||
|
||||
return NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count);
|
||||
}
|
||||
|
||||
public ArraySegment<byte> GetBytesSegment(int count)
|
||||
{
|
||||
ArraySegment<byte> segment = new(_data, _position, count);
|
||||
_position += count;
|
||||
return segment;
|
||||
}
|
||||
|
||||
public ArraySegment<byte> GetRemainingBytesSegment()
|
||||
{
|
||||
ArraySegment<byte> segment = new(_data, _position, AvailableBytes);
|
||||
_position = _data.Length;
|
||||
return segment;
|
||||
}
|
||||
|
||||
public T Get<T>() where T : struct, INetSerializable
|
||||
{
|
||||
T obj = default(T);
|
||||
obj.Deserialize(this);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public T Get<T>(Func<T> constructor) where T : class, INetSerializable
|
||||
{
|
||||
T obj = constructor();
|
||||
obj.Deserialize(this);
|
||||
return obj;
|
||||
}
|
||||
|
||||
#if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1
|
||||
public ReadOnlySpan<byte> GetRemainingBytesSpan()
|
||||
{
|
||||
return new(_data, _position, _dataSize - _position);
|
||||
}
|
||||
#endif
|
||||
|
||||
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()
|
||||
{
|
||||
return GetArray<sbyte>(1);
|
||||
}
|
||||
|
||||
public byte[] GetBytesWithLength()
|
||||
{
|
||||
return GetArray<byte>(1);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PeekMethods
|
||||
public byte PeekByte()
|
||||
{
|
||||
return _data[_position];
|
||||
}
|
||||
|
||||
public sbyte PeekSByte()
|
||||
{
|
||||
return (sbyte)_data[_position];
|
||||
}
|
||||
|
||||
public bool PeekBool()
|
||||
{
|
||||
return _data[_position] == 1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Note that "maxLength" only limits the number of characters in a string, not its size in bytes.
|
||||
/// </summary>
|
||||
public string PeekString(int maxLength)
|
||||
{
|
||||
ushort size = PeekUShort();
|
||||
if (size == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
int actualSize = size - 1;
|
||||
if (actualSize >= NetDataWriter.StringBufferMaxLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position + 2, actualSize) > maxLength ? string.Empty : NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize);
|
||||
}
|
||||
|
||||
public string PeekString()
|
||||
{
|
||||
ushort size = PeekUShort();
|
||||
if (size == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
int actualSize = size - 1;
|
||||
if (actualSize >= NetDataWriter.StringBufferMaxLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return NetDataWriter.uTF8Encoding.Value.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)
|
||||
{
|
||||
if (!TryGetUShort(out ushort 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 >= 2)
|
||||
{
|
||||
ushort length = PeekUShort();
|
||||
if (length >= 0 && AvailableBytes >= 2 + length)
|
||||
{
|
||||
result = GetBytesWithLength();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_position = 0;
|
||||
_dataSize = 0;
|
||||
_data = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2876e12b475627f448ca5a6850748bc3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs
|
||||
uploadId: 866910
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user