using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using LiteNetLib.Layers; using LiteNetLib.Utils; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Ruslan Pyrch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright 2024 Ruslan Pyrch")] [assembly: AssemblyDescription("Lite reliable UDP library for .NET, Mono, and .NET Core")] [assembly: AssemblyFileVersion("1.3.5")] [assembly: AssemblyInformationalVersion("1.0.0+cf54c7d7a15a45bd2460cd4a460807329ee7026a")] [assembly: AssemblyProduct("LiteNetLib")] [assembly: AssemblyTitle("LiteNetLib")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/RevenantX/LiteNetLib")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.5.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsUnmanagedAttribute : Attribute { } } namespace LiteNetLib { internal abstract class BaseChannel { protected readonly NetPeer Peer; protected readonly Queue OutgoingQueue = new Queue(64); 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 num = SendNextPackets(); if (!num) { Interlocked.Exchange(ref _isAddedToPeerChannelSendQueue, 0); } return num; } protected abstract bool SendNextPackets(); public abstract bool ProcessPacket(NetPacket packet); } internal enum ConnectionRequestResult { None, Accept, Reject, RejectForce } public class ConnectionRequest { private readonly NetManager _listener; private int _used; internal NetConnectRequestPacket InternalPacket; public readonly IPEndPoint RemoteEndPoint; public NetDataReader Data => InternalPacket.Data; internal ConnectionRequestResult Result { get; private set; } internal void UpdateRequest(NetConnectRequestPacket connectRequest) { if (connectRequest.ConnectionTime >= InternalPacket.ConnectionTime && (connectRequest.ConnectionTime != InternalPacket.ConnectionTime || connectRequest.ConnectionNumber != InternalPacket.ConnectionNumber)) { 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; } 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()) { 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, force: false); } public void RejectForce(byte[] rejectData, int start, int length) { Reject(rejectData, start, length, force: true); } public void RejectForce() { Reject(null, 0, 0, force: true); } public void RejectForce(byte[] rejectData) { Reject(rejectData, 0, rejectData.Length, force: true); } public void RejectForce(NetDataWriter rejectData) { Reject(rejectData.Data, 0, rejectData.Length, force: true); } public void Reject() { Reject(null, 0, 0, force: false); } public void Reject(byte[] rejectData) { Reject(rejectData, 0, rejectData.Length, force: false); } public void Reject(NetDataWriter rejectData) { Reject(rejectData.Data, 0, rejectData.Length, force: false); } } public enum UnconnectedMessageType { BasicMessage, Broadcast } public enum DisconnectReason { ConnectionFailed, Timeout, HostUnreachable, NetworkUnreachable, RemoteConnectionClose, DisconnectPeerCalled, ConnectionRejected, InvalidProtocol, UnknownHost, Reconnect, PeerToPeerConnection, PeerNotFound } public struct DisconnectInfo { public DisconnectReason Reason; public SocketError SocketErrorCode; public NetPacketReader AdditionalData; } public interface INetEventListener { void OnPeerConnected(NetPeer peer); void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); void OnNetworkError(IPEndPoint endPoint, SocketError socketError); void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod); void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); void OnNetworkLatencyUpdate(NetPeer peer, int latency); void OnConnectionRequest(ConnectionRequest request); } public interface IDeliveryEventListener { void OnMessageDelivered(NetPeer peer, object userData); } public interface INtpEventListener { void OnNtpResponse(NtpPacket packet); } public interface IPeerAddressChangedListener { 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() { this.PeerConnectedEvent = null; } public void ClearPeerDisconnectedEvent() { this.PeerDisconnectedEvent = null; } public void ClearNetworkErrorEvent() { this.NetworkErrorEvent = null; } public void ClearNetworkReceiveEvent() { this.NetworkReceiveEvent = null; } public void ClearNetworkReceiveUnconnectedEvent() { this.NetworkReceiveUnconnectedEvent = null; } public void ClearNetworkLatencyUpdateEvent() { this.NetworkLatencyUpdateEvent = null; } public void ClearConnectionRequestEvent() { this.ConnectionRequestEvent = null; } public void ClearDeliveryEvent() { this.DeliveryEvent = null; } public void ClearNtpResponseEvent() { this.NtpResponseEvent = null; } public void ClearPeerAddressChangedEvent() { this.PeerAddressChangedEvent = null; } void INetEventListener.OnPeerConnected(NetPeer peer) { if (this.PeerConnectedEvent != null) { this.PeerConnectedEvent(peer); } } void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { if (this.PeerDisconnectedEvent != null) { this.PeerDisconnectedEvent(peer, disconnectInfo); } } void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) { if (this.NetworkErrorEvent != null) { this.NetworkErrorEvent(endPoint, socketErrorCode); } } void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) { if (this.NetworkReceiveEvent != null) { this.NetworkReceiveEvent(peer, reader, channelNumber, deliveryMethod); } } void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { if (this.NetworkReceiveUnconnectedEvent != null) { this.NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType); } } void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) { if (this.NetworkLatencyUpdateEvent != null) { this.NetworkLatencyUpdateEvent(peer, latency); } } void INetEventListener.OnConnectionRequest(ConnectionRequest request) { if (this.ConnectionRequestEvent != null) { this.ConnectionRequestEvent(request); } } void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData) { if (this.DeliveryEvent != null) { this.DeliveryEvent(peer, userData); } } void INtpEventListener.OnNtpResponse(NtpPacket packet) { if (this.NtpResponseEvent != null) { this.NtpResponseEvent(packet); } } void IPeerAddressChangedListener.OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress) { if (this.PeerAddressChangedEvent != null) { this.PeerAddressChangedEvent(peer, previousAddress); } } } 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 >= 4) { return null; } long connectionTime = BitConverter.ToInt64(packet.RawData, 5); int localId = BitConverter.ToInt32(packet.RawData, 13); int num = packet.RawData[17]; if (num != 16 && num != 28) { return null; } byte[] array = new byte[num]; Buffer.BlockCopy(packet.RawData, 18, array, 0, num); NetDataReader netDataReader = new NetDataReader(null, 0, 0); if (packet.Size > 18 + num) { netDataReader.SetSource(packet.RawData, 18 + num, packet.Size); } return new NetConnectRequestPacket(connectionTime, packet.ConnectionNumber, localId, array, netDataReader); } public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectTime, int localId) { NetPacket netPacket = new NetPacket(PacketProperty.ConnectRequest, connectData.Length + addressBytes.Size); FastBitConverter.GetBytes(netPacket.RawData, 1, 13); FastBitConverter.GetBytes(netPacket.RawData, 5, connectTime); FastBitConverter.GetBytes(netPacket.RawData, 13, localId); netPacket.RawData[17] = (byte)addressBytes.Size; for (int i = 0; i < addressBytes.Size; i++) { netPacket.RawData[18 + i] = addressBytes[i]; } Buffer.BlockCopy(connectData.Data, 0, netPacket.RawData, 18 + addressBytes.Size, connectData.Length); return netPacket; } public static NetPacket Make(ReadOnlySpan connectData, SocketAddress addressBytes, long connectTime, int localId) { NetPacket netPacket = new NetPacket(PacketProperty.ConnectRequest, connectData.Length + addressBytes.Size); FastBitConverter.GetBytes(netPacket.RawData, 1, 13); FastBitConverter.GetBytes(netPacket.RawData, 5, connectTime); FastBitConverter.GetBytes(netPacket.RawData, 13, localId); netPacket.RawData[17] = (byte)addressBytes.Size; for (int i = 0; i < addressBytes.Size; i++) { netPacket.RawData[18 + i] = addressBytes[i]; } connectData.CopyTo(netPacket.RawData.AsSpan(18 + addressBytes.Size)); return netPacket; } } 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 != 15) { return null; } long connectionTime = BitConverter.ToInt64(packet.RawData, 1); byte b = packet.RawData[9]; if (b >= 4) { return null; } byte b2 = packet.RawData[10]; if (b2 > 1) { return null; } int num = BitConverter.ToInt32(packet.RawData, 11); if (num < 0) { return null; } return new NetConnectAcceptPacket(connectionTime, b, num, b2 == 1); } public static NetPacket Make(long connectTime, byte connectNum, int localPeerId) { NetPacket netPacket = new NetPacket(PacketProperty.ConnectAccept, 0); FastBitConverter.GetBytes(netPacket.RawData, 1, connectTime); netPacket.RawData[9] = connectNum; FastBitConverter.GetBytes(netPacket.RawData, 11, localPeerId); return netPacket; } public static NetPacket MakeNetworkChanged(NetPeer peer) { NetPacket netPacket = new NetPacket(PacketProperty.PeerNotFound, 14); FastBitConverter.GetBytes(netPacket.RawData, 1, peer.ConnectTime); netPacket.RawData[9] = peer.ConnectionNum; netPacket.RawData[10] = 1; FastBitConverter.GetBytes(netPacket.RawData, 11, peer.RemoteId); return netPacket; } } internal static class NativeSocket { private static class WinSock { private const string LibName = "ws2_32.dll"; [DllImport("ws2_32.dll", 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("ws2_32.dll", SetLastError = true)] internal unsafe static extern int sendto(IntPtr socketHandle, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize); } private static class UnixSock { private const string LibName = "libc"; [DllImport("libc", 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("libc", SetLastError = true)] internal unsafe 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; public static readonly bool UnixMode; 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 NativeErrorToSocketError; static NativeSocket() { IsSupported = false; UnixMode = false; NativeErrorToSocketError = new Dictionary { { 13, SocketError.AccessDenied }, { 98, SocketError.AddressAlreadyInUse }, { 99, SocketError.AddressNotAvailable }, { 97, SocketError.AddressFamilyNotSupported }, { 11, SocketError.WouldBlock }, { 114, SocketError.AlreadyInProgress }, { 9, SocketError.OperationAborted }, { 125, SocketError.OperationAborted }, { 103, SocketError.ConnectionAborted }, { 111, SocketError.ConnectionRefused }, { 104, SocketError.ConnectionReset }, { 89, SocketError.DestinationAddressRequired }, { 14, SocketError.Fault }, { 112, SocketError.HostDown }, { 6, SocketError.HostNotFound }, { 113, SocketError.HostUnreachable }, { 115, SocketError.InProgress }, { 4, SocketError.Interrupted }, { 22, SocketError.InvalidArgument }, { 106, SocketError.IsConnected }, { 24, SocketError.TooManyOpenSockets }, { 90, SocketError.MessageSize }, { 100, SocketError.NetworkDown }, { 102, SocketError.NetworkReset }, { 101, SocketError.NetworkUnreachable }, { 23, SocketError.TooManyOpenSockets }, { 105, SocketError.NoBufferSpaceAvailable }, { 61, SocketError.NoData }, { 2, SocketError.AddressNotAvailable }, { 92, SocketError.ProtocolOption }, { 107, SocketError.NotConnected }, { 88, SocketError.NotSocket }, { 3440, SocketError.OperationNotSupported }, { 1, SocketError.AccessDenied }, { 32, SocketError.Shutdown }, { 96, SocketError.ProtocolFamilyNotSupported }, { 93, SocketError.ProtocolNotSupported }, { 91, SocketError.ProtocolType }, { 94, SocketError.SocketNotSupported }, { 108, SocketError.Disconnecting }, { 110, SocketError.TimedOut }, { 0, SocketError.Success } }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { IsSupported = true; UnixMode = true; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { IsSupported = true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int RecvFrom(IntPtr socketHandle, byte[] pinnedBuffer, int len, byte[] socketAddress, ref int socketAddressSize) { if (!UnixMode) { return WinSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize); } return UnixSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static int SendTo(IntPtr socketHandle, byte* pinnedBuffer, int len, byte[] socketAddress, int socketAddressSize) { if (!UnixMode) { return WinSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize); } return UnixSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize); } public static SocketError GetSocketError() { int lastWin32Error = Marshal.GetLastWin32Error(); if (UnixMode) { if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value)) { return SocketError.SocketError; } return value; } return (SocketError)lastWin32Error; } public static SocketException GetSocketException() { int lastWin32Error = Marshal.GetLastWin32Error(); if (UnixMode) { if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value)) { return new SocketException(-1); } return new SocketException((int)value); } return new SocketException(lastWin32Error); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint) { if (!UnixMode) { return (short)remoteEndPoint.AddressFamily; } return (short)((remoteEndPoint.AddressFamily == AddressFamily.InterNetwork) ? 2 : 10); } } 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 (this.NatIntroductionRequest != null) { this.NatIntroductionRequest(localEndPoint, remoteEndPoint, token); } } void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token) { if (this.NatIntroductionSuccess != null) { this.NatIntroductionSuccess(targetEndPoint, type, token); } } } 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 _requestEvents = new ConcurrentQueue(); private readonly ConcurrentQueue _successEvents = new ConcurrentQueue(); private readonly NetDataReader _cacheReader = new NetDataReader(); private readonly NetDataWriter _cacheWriter = new NetDataWriter(); private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(256); private INatPunchListener _natPunchListener; public const int MaxTokenLength = 256; public bool UnsyncedEvents; internal NatPunchModule(NetManager socket) { _socket = socket; _netPacketProcessor.SubscribeReusable(OnNatIntroductionResponse); _netPacketProcessor.SubscribeReusable(OnNatIntroductionRequest); _netPacketProcessor.SubscribeReusable(OnNatPunch); } internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet) { lock (_cacheReader) { _cacheReader.SetSource(packet.RawData, 1, packet.Size); _netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint); } } public void Init(INatPunchListener listener) { _natPunchListener = listener; } private void Send(T packet, IPEndPoint target) where T : class, new() { _cacheWriter.Reset(); _cacheWriter.Put((byte)16); _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 natIntroduceResponsePacket = new NatIntroduceResponsePacket { Token = additionalInfo }; natIntroduceResponsePacket.Internal = hostInternal; natIntroduceResponsePacket.External = hostExternal; Send(natIntroduceResponsePacket, clientExternal); natIntroduceResponsePacket.Internal = clientInternal; natIntroduceResponsePacket.External = clientExternal; Send(natIntroduceResponsePacket, hostExternal); } public void PollEvents() { if (!UnsyncedEvents && _natPunchListener != null && (!_successEvents.IsEmpty || !_requestEvents.IsEmpty)) { SuccessEventData result; while (_successEvents.TryDequeue(out result)) { _natPunchListener.OnNatIntroductionSuccess(result.TargetEndPoint, result.Type, result.Token); } RequestEventData result2; while (_requestEvents.TryDequeue(out result2)) { _natPunchListener.OnNatIntroductionRequest(result2.LocalEndPoint, result2.RemoteEndPoint, result2.Token); } } } public void SendNatIntroduceRequest(string host, int port, string additionalInfo) { SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo); } public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo) { string localIp = NetUtils.GetLocalIp(LocalAddrType.IPv4); if (string.IsNullOrEmpty(localIp) || masterServerEndPoint.AddressFamily == AddressFamily.InterNetworkV6) { localIp = NetUtils.GetLocalIp(LocalAddrType.IPv6); } Send(new NatIntroduceRequestPacket { Internal = NetUtils.MakeEndPoint(localIp, _socket.LocalPort), Token = additionalInfo }, masterServerEndPoint); } private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint) { if (UnsyncedEvents) { _natPunchListener.OnNatIntroductionRequest(req.Internal, senderEndPoint, req.Token); return; } _requestEvents.Enqueue(new RequestEventData { LocalEndPoint = req.Internal, RemoteEndPoint = senderEndPoint, Token = req.Token }); } private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) { NatPunchPacket natPunchPacket = new NatPunchPacket { Token = req.Token }; Send(natPunchPacket, req.Internal); _socket.Ttl = 2; _socket.SendRaw(new byte[1] { 17 }, 0, 1, req.External); _socket.Ttl = 255; natPunchPacket.IsExternal = true; Send(natPunchPacket, req.External); } private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint) { if (UnsyncedEvents) { _natPunchListener.OnNatIntroductionSuccess(senderEndPoint, req.IsExternal ? NatAddressType.External : NatAddressType.Internal, req.Token); return; } _successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Type = (req.IsExternal ? NatAddressType.External : NatAddressType.Internal), Token = req.Token }); } } public enum DeliveryMethod : byte { Unreliable = 4, ReliableUnordered = 0, Sequenced = 1, ReliableOrdered = 2, ReliableSequenced = 3 } public static class NetConstants { public const int DefaultWindowSize = 64; public const int SocketBufferSize = 1048576; public const int SocketTTL = 255; public const int HeaderSize = 1; public const int ChanneledHeaderSize = 4; public const int FragmentHeaderSize = 6; public const int FragmentedHeaderTotalSize = 10; public const ushort MaxSequence = 32768; public const ushort HalfMaxSequence = 16384; internal const int ProtocolId = 13; internal const int MaxUdpHeaderSize = 68; internal const int ChannelTypeCount = 4; internal const int FragmentedChannelsCount = 2; internal const int MaxFragmentsInWindow = 32; internal static readonly int[] PossibleMtu = new int[6] { 1024, 1164, 1392, 1404, 1424, 1432 }; public static readonly int InitialMtu = PossibleMtu[0]; public static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1]; public static readonly int MaxUnreliableDataSize = MaxPacketSize - 1; public const byte MaxConnectionNumber = 4; } 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 } public interface INetLogger { void WriteNet(NetLogLevel level, string str, params object[] args); } public static class NetDebug { public static INetLogger Logger = null; private static readonly object DebugLogLock = new object(); private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args) { lock (DebugLogLock) { if (Logger == null) { Console.WriteLine(str, args); } 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); } } public sealed class NetPacketReader : NetDataReader { private NetPacket _packet; private readonly NetManager _manager; private readonly NetEvent _evt; internal NetPacketReader(NetManager manager, NetEvent evt) { _manager = manager; _evt = evt; } internal void SetSource(NetPacket packet, int headerSize) { if (packet != null) { _packet = packet; SetSource(packet.RawData, headerSize, packet.Size); } } internal void RecycleInternal() { Clear(); if (_packet != null) { _manager.PoolRecycle(_packet); } _packet = null; _manager.RecycleEvent(_evt); } public void Recycle() { if (!_manager.AutoRecycle) { RecycleInternal(); } } } internal sealed class NetEvent { public enum EType { Connect, Disconnect, Receive, ReceiveUnconnected, Error, ConnectionLatencyUpdated, Broadcast, ConnectionRequest, MessageDelivered, PeerAddressChanged } public NetEvent Next; public EType Type; public NetPeer Peer; public IPEndPoint RemoteEndPoint; public object UserData; public int Latency; public SocketError ErrorCode; public DisconnectReason DisconnectReason; public ConnectionRequest ConnectionRequest; public DeliveryMethod DeliveryMethod; public byte ChannelNumber; public readonly NetPacketReader DataReader; public NetEvent(NetManager manager) { DataReader = new NetPacketReader(manager, this); } } public class NetManager : IEnumerable, IEnumerable { public struct NetPeerEnumerator : IEnumerator, IEnumerator, IDisposable { private readonly NetPeer _initialPeer; private NetPeer _p; public NetPeer Current => _p; object IEnumerator.Current => _p; public NetPeerEnumerator(NetPeer p) { _initialPeer = p; _p = null; } public void Dispose() { } public bool MoveNext() { _p = ((_p == null) ? _initialPeer : _p.NextPeer); return _p != null; } public void Reset() { throw new NotSupportedException(); } } private struct IncomingData { public NetPacket Data; public IPEndPoint EndPoint; public DateTime TimeWhenGet; } private struct OutboundDelayedPacket { public byte[] Data; public int Start; public int Length; public IPEndPoint EndPoint; public DateTime TimeWhenSend; } private struct Slot { internal int HashCode; internal int Next; internal NetPeer Value; } private readonly List _pingSimulationList = new List(); private readonly List _outboundSimulationList = new List(); private readonly Random _randomGenerator = new Random(); private const int MinLatencyThreshold = 5; private Thread _logicThread; private bool _manualMode; private readonly AutoResetEvent _updateTriggerEvent = new AutoResetEvent(initialState: true); private NetEvent _pendingEventHead; private NetEvent _pendingEventTail; private NetEvent _netEventPoolHead; private readonly INetEventListener _netEventListener; private readonly IDeliveryEventListener _deliveryEventListener; private readonly INtpEventListener _ntpEventListener; private readonly IPeerAddressChangedListener _peerAddressChangedListener; private readonly Dictionary _requestsDict = new Dictionary(); private readonly ConcurrentDictionary _ntpRequests = new ConcurrentDictionary(); private long _connectedPeersCount; private readonly List _connectedPeerListCache = new List(); private readonly PacketLayerBase _extraPacketLayer; private int _lastPeerId; private ConcurrentQueue _peerIds = new ConcurrentQueue(); private byte _channelsCount = 1; private readonly object _eventLock = new object(); private volatile bool _isRunning; private bool _dropPacket; public bool UnconnectedMessagesEnabled; public bool NatPunchEnabled; public int UpdateTime = 15; public int PingInterval = 1000; public int DisconnectTimeout = 5000; public bool SimulatePacketLoss; public bool SimulateLatency; public int SimulationPacketLossChance = 10; public int SimulationMinLatency = 30; public int SimulationMaxLatency = 100; public bool UnsyncedEvents; public bool UnsyncedReceiveEvent; public bool UnsyncedDeliveryEvent; public bool BroadcastReceiveEnabled; public int ReconnectDelay = 500; public int MaxConnectAttempts = 10; public bool ReuseAddress; public bool DontRoute; public readonly NetStatistics Statistics = new NetStatistics(); public bool EnableStatistics; public readonly NatPunchModule NatPunchModule; public bool AutoRecycle; public bool IPv6Enabled = true; public int MtuOverride; public bool MtuDiscovery; public bool UseNativeSockets; public bool DisconnectOnUnreachable; public bool AllowPeerAddressChange; private const int MaxPrimeArrayLength = 2147483587; private const int HashPrime = 101; private const int Lower31BitMask = int.MaxValue; private static readonly int[] Primes; 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 ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private volatile NetPeer _headPeer; private NetPacket _poolHead; private int _poolCount; private readonly object _poolLock = new object(); public int PacketPoolSize = 1000; private Socket _udpSocketv4; private Socket _udpSocketv6; private Thread _receiveThread; private IPEndPoint _bufferEndPointv4; private IPEndPoint _bufferEndPointv6; private const int SioUdpConnreset = -1744830452; private static readonly IPAddress MulticastAddressV6; public static readonly bool IPv6Support; internal bool NotConnected; public int ReceivePollingTime = 50000; public bool IsRunning => _isRunning; public int LocalPort { get; private set; } public NetPeer FirstPeer => _headPeer; public byte ChannelsCount { get { return _channelsCount; } set { if (value < 1 || value > 64) { throw new ArgumentException("Channels count must be between 1 and 64"); } _channelsCount = value; } } public List ConnectedPeerList { get { GetPeersNonAlloc(_connectedPeerListCache, ConnectionState.Connected); return _connectedPeerListCache; } } public int ConnectedPeersCount => (int)Interlocked.Read(ref _connectedPeersCount); public int ExtraPacketSizeForLayer => _extraPacketLayer?.ExtraPacketSizeForLayer ?? 0; public int PoolCount => _poolCount; public short Ttl { get { return _udpSocketv4.Ttl; } internal set { _udpSocketv4.Ttl = value; } } public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null) { _netEventListener = listener; _deliveryEventListener = listener as IDeliveryEventListener; _ntpEventListener = listener as INtpEventListener; _peerAddressChangedListener = listener as IPeerAddressChangedListener; NatPunchModule = new NatPunchModule(this); _extraPacketLayer = extraPacketLayer; } internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency) { CreateEvent(NetEvent.EType.ConnectionLatencyUpdated, fromPeer, null, SocketError.Success, latency, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } internal void MessageDelivered(NetPeer fromPeer, object userData) { if (_deliveryEventListener != null) { CreateEvent(NetEvent.EType.MessageDelivered, fromPeer, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, null, userData); } } internal void DisconnectPeerForce(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, NetPacket eventData) { DisconnectPeer(peer, reason, socketErrorCode, force: true, null, 0, 0, eventData); } private void DisconnectPeer(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, bool force, byte[] data, int start, int count, NetPacket eventData) { switch (peer.Shutdown(data, start, count, force)) { case ShutdownResult.None: return; case ShutdownResult.WasConnected: Interlocked.Decrement(ref _connectedPeersCount); break; } CreateEvent(NetEvent.EType.Disconnect, peer, null, socketErrorCode, 0, reason, null, DeliveryMethod.Unreliable, 0, eventData); } private void CreateEvent(NetEvent.EType type, NetPeer peer = null, IPEndPoint remoteEndPoint = null, SocketError errorCode = SocketError.Success, int latency = 0, DisconnectReason disconnectReason = DisconnectReason.ConnectionFailed, ConnectionRequest connectionRequest = null, DeliveryMethod deliveryMethod = DeliveryMethod.Unreliable, byte channelNumber = 0, NetPacket readerSource = null, object userData = null) { bool flag = UnsyncedEvents; switch (type) { case NetEvent.EType.Connect: Interlocked.Increment(ref _connectedPeersCount); break; case NetEvent.EType.MessageDelivered: flag = UnsyncedDeliveryEvent; break; } NetEvent netEvent; lock (_eventLock) { netEvent = _netEventPoolHead; if (netEvent == null) { netEvent = new NetEvent(this); } else { _netEventPoolHead = netEvent.Next; } } netEvent.Next = null; netEvent.Type = type; netEvent.DataReader.SetSource(readerSource, readerSource?.GetHeaderSize() ?? 0); netEvent.Peer = peer; netEvent.RemoteEndPoint = remoteEndPoint; netEvent.Latency = latency; netEvent.ErrorCode = errorCode; netEvent.DisconnectReason = disconnectReason; netEvent.ConnectionRequest = connectionRequest; netEvent.DeliveryMethod = deliveryMethod; netEvent.ChannelNumber = channelNumber; netEvent.UserData = userData; if (flag || _manualMode) { ProcessEvent(netEvent); return; } lock (_eventLock) { if (_pendingEventTail == null) { _pendingEventHead = netEvent; } else { _pendingEventTail.Next = netEvent; } _pendingEventTail = netEvent; } } private void ProcessEvent(NetEvent evt) { bool isNull = evt.DataReader.IsNull; switch (evt.Type) { case NetEvent.EType.Connect: _netEventListener.OnPeerConnected(evt.Peer); break; case NetEvent.EType.Disconnect: { DisconnectInfo disconnectInfo = default(DisconnectInfo); disconnectInfo.Reason = evt.DisconnectReason; disconnectInfo.AdditionalData = evt.DataReader; disconnectInfo.SocketErrorCode = evt.ErrorCode; DisconnectInfo disconnectInfo2 = disconnectInfo; _netEventListener.OnPeerDisconnected(evt.Peer, disconnectInfo2); break; } case NetEvent.EType.Receive: _netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader, evt.ChannelNumber, evt.DeliveryMethod); break; case NetEvent.EType.ReceiveUnconnected: _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.BasicMessage); break; case NetEvent.EType.Broadcast: _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.Broadcast); break; case NetEvent.EType.Error: _netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.ErrorCode); break; case NetEvent.EType.ConnectionLatencyUpdated: _netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.Latency); break; case NetEvent.EType.ConnectionRequest: _netEventListener.OnConnectionRequest(evt.ConnectionRequest); break; case NetEvent.EType.MessageDelivered: _deliveryEventListener.OnMessageDelivered(evt.Peer, evt.UserData); break; case NetEvent.EType.PeerAddressChanged: { _peersLock.EnterUpgradeableReadLock(); IPEndPoint iPEndPoint = null; if (ContainsPeer(evt.Peer)) { _peersLock.EnterWriteLock(); RemovePeerFromSet(evt.Peer); iPEndPoint = new IPEndPoint(evt.Peer.Address, evt.Peer.Port); evt.Peer.FinishEndPointChange(evt.RemoteEndPoint); AddPeerToSet(evt.Peer); _peersLock.ExitWriteLock(); } _peersLock.ExitUpgradeableReadLock(); if (iPEndPoint != null && _peerAddressChangedListener != null) { _peerAddressChangedListener.OnPeerAddressChanged(evt.Peer, iPEndPoint); } break; } } if (isNull) { RecycleEvent(evt); } else if (AutoRecycle) { evt.DataReader.RecycleInternal(); } } internal void RecycleEvent(NetEvent evt) { evt.Peer = null; evt.ErrorCode = SocketError.Success; evt.RemoteEndPoint = null; evt.ConnectionRequest = null; lock (_eventLock) { evt.Next = _netEventPoolHead; _netEventPoolHead = evt; } } private void UpdateLogic() { List list = new List(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); while (_isRunning) { try { float num = (float)((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0); num = ((num <= 0f) ? 0.001f : num); stopwatch.Restart(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout) { list.Add(netPeer); } else { netPeer.Update(num); } } if (list.Count > 0) { _peersLock.EnterWriteLock(); for (int i = 0; i < list.Count; i++) { RemovePeer(list[i], enableWriteLock: false); } _peersLock.ExitWriteLock(); list.Clear(); } ProcessNtpRequests(num); int num2 = UpdateTime - (int)stopwatch.ElapsedMilliseconds; if (num2 > 0) { _updateTriggerEvent.WaitOne(num2); } } catch (ThreadAbortException) { return; } catch (Exception ex2) { NetDebug.WriteError("[NM] LogicThread error: " + ex2); } } stopwatch.Stop(); } [Conditional("DEBUG")] [Conditional("SIMULATE_NETWORK")] private void ProcessDelayedPackets() { if (!SimulateLatency) { return; } DateTime utcNow = DateTime.UtcNow; lock (_pingSimulationList) { for (int i = 0; i < _pingSimulationList.Count; i++) { IncomingData incomingData = _pingSimulationList[i]; if (incomingData.TimeWhenGet <= utcNow) { HandleMessageReceived(incomingData.Data, incomingData.EndPoint); _pingSimulationList.RemoveAt(i); i--; } } } lock (_outboundSimulationList) { for (int j = 0; j < _outboundSimulationList.Count; j++) { OutboundDelayedPacket outboundDelayedPacket = _outboundSimulationList[j]; if (outboundDelayedPacket.TimeWhenSend <= utcNow) { SendRawCore(outboundDelayedPacket.Data, outboundDelayedPacket.Start, outboundDelayedPacket.Length, outboundDelayedPacket.EndPoint); _outboundSimulationList.RemoveAt(j); j--; } } } } private void ProcessNtpRequests(float elapsedMilliseconds) { if (_ntpRequests.IsEmpty) { return; } List list = null; foreach (KeyValuePair ntpRequest in _ntpRequests) { ntpRequest.Value.Send(_udpSocketv4, elapsedMilliseconds); if (ntpRequest.Value.NeedToKill) { if (list == null) { list = new List(); } list.Add(ntpRequest.Key); } } if (list == null) { return; } foreach (IPEndPoint item in list) { _ntpRequests.TryRemove(item, out var _); } } public void ManualUpdate(float elapsedMilliseconds) { if (!_manualMode) { return; } for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout) { RemovePeer(netPeer, enableWriteLock: false); } else { netPeer.Update(elapsedMilliseconds); } } ProcessNtpRequests(elapsedMilliseconds); } internal NetPeer OnConnectionSolved(ConnectionRequest request, byte[] rejectData, int start, int length) { NetPeer actualValue = null; if (request.Result == ConnectionRequestResult.RejectForce) { if (rejectData != null && length > 0) { NetPacket netPacket = PoolGetWithProperty(PacketProperty.Disconnect, length); netPacket.ConnectionNumber = request.InternalPacket.ConnectionNumber; FastBitConverter.GetBytes(netPacket.RawData, 1, request.InternalPacket.ConnectionTime); if (netPacket.Size >= NetConstants.PossibleMtu[0]) { NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU!"); } else { Buffer.BlockCopy(rejectData, start, netPacket.RawData, 9, length); } SendRawAndRecycle(netPacket, request.RemoteEndPoint); } lock (_requestsDict) { _requestsDict.Remove(request.RemoteEndPoint); } } else { lock (_requestsDict) { if (!TryGetPeer(request.RemoteEndPoint, out actualValue)) { if (request.Result == ConnectionRequestResult.Reject) { actualValue = new NetPeer(this, request.RemoteEndPoint, GetNextPeerId()); actualValue.Reject(request.InternalPacket, rejectData, start, length); AddPeer(actualValue); } else { actualValue = new NetPeer(this, request, GetNextPeerId()); AddPeer(actualValue); CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } } _requestsDict.Remove(request.RemoteEndPoint); } } return actualValue; } private int GetNextPeerId() { if (!_peerIds.TryDequeue(out var result)) { return _lastPeerId++; } return result; } private void ProcessConnectRequest(IPEndPoint remoteEndPoint, NetPeer netPeer, NetConnectRequestPacket connRequest) { if (netPeer != null) { ConnectRequestResult connectRequestResult = netPeer.ProcessConnectRequest(connRequest); switch (connectRequestResult) { default: return; case ConnectRequestResult.Reconnection: DisconnectPeerForce(netPeer, DisconnectReason.Reconnect, SocketError.Success, null); RemovePeer(netPeer, enableWriteLock: true); break; case ConnectRequestResult.NewConnection: RemovePeer(netPeer, enableWriteLock: true); break; case ConnectRequestResult.P2PLose: DisconnectPeerForce(netPeer, DisconnectReason.PeerToPeerConnection, SocketError.Success, null); RemovePeer(netPeer, enableWriteLock: true); break; } if (connectRequestResult != ConnectRequestResult.P2PLose) { connRequest.ConnectionNumber = (byte)((netPeer.ConnectionNum + 1) % 4); } } ConnectionRequest value; lock (_requestsDict) { if (_requestsDict.TryGetValue(remoteEndPoint, out value)) { value.UpdateRequest(connRequest); return; } value = new ConnectionRequest(remoteEndPoint, connRequest, this); _requestsDict.Add(remoteEndPoint, value); } CreateEvent(NetEvent.EType.ConnectionRequest, null, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, value, DeliveryMethod.Unreliable, 0); } private void OnMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint) { if (packet.Size == 0) { PoolRecycle(packet); return; } _dropPacket = false; if (!_dropPacket) { HandleMessageReceived(packet, remoteEndPoint); } } [Conditional("DEBUG")] [Conditional("SIMULATE_NETWORK")] private void HandleSimulateLatency(NetPacket packet, IPEndPoint remoteEndPoint) { if (!SimulateLatency) { return; } int num = _randomGenerator.Next(SimulationMinLatency, SimulationMaxLatency) / 2; if (num > 5) { lock (_pingSimulationList) { _pingSimulationList.Add(new IncomingData { Data = packet, EndPoint = remoteEndPoint, TimeWhenGet = DateTime.UtcNow.AddMilliseconds(num) }); } _dropPacket = true; } } [Conditional("DEBUG")] [Conditional("SIMULATE_NETWORK")] private void HandleSimulatePacketLoss() { if (SimulatePacketLoss && _randomGenerator.NextDouble() * 100.0 < (double)SimulationPacketLossChance) { _dropPacket = true; } } private void HandleMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint) { int size = packet.Size; if (EnableStatistics) { Statistics.IncrementPacketsReceived(); Statistics.AddBytesReceived(size); } if (_ntpRequests.Count > 0 && _ntpRequests.TryGetValue(remoteEndPoint, out var _)) { if (packet.Size >= 48) { byte[] array = new byte[packet.Size]; Buffer.BlockCopy(packet.RawData, 0, array, 0, packet.Size); NtpPacket ntpPacket = NtpPacket.FromServerResponse(array, DateTime.UtcNow); try { ntpPacket.ValidateReply(); } catch (InvalidOperationException) { ntpPacket = null; } if (ntpPacket != null) { _ntpRequests.TryRemove(remoteEndPoint, out var _); _ntpEventListener?.OnNtpResponse(ntpPacket); } } return; } if (_extraPacketLayer != null) { _extraPacketLayer.ProcessInboundPacket(ref remoteEndPoint, ref packet.RawData, ref packet.Size); if (packet.Size == 0) { return; } } if (!packet.Verify()) { NetDebug.WriteError("[NM] DataReceived: bad!"); PoolRecycle(packet); return; } switch (packet.Property) { case PacketProperty.ConnectRequest: if (NetConnectRequestPacket.GetProtocolId(packet) != 13) { SendRawAndRecycle(PoolGetWithProperty(PacketProperty.InvalidProtocol), remoteEndPoint); return; } break; case PacketProperty.Broadcast: if (BroadcastReceiveEnabled) { CreateEvent(NetEvent.EType.Broadcast, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet); } return; case PacketProperty.UnconnectedMessage: if (UnconnectedMessagesEnabled) { CreateEvent(NetEvent.EType.ReceiveUnconnected, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet); } return; case PacketProperty.NatMessage: if (NatPunchEnabled) { NatPunchModule.ProcessMessage(remoteEndPoint, packet); } return; } NetPeer actualValue = remoteEndPoint as NetPeer; bool flag = actualValue != null || TryGetPeer(remoteEndPoint, out actualValue); if (flag && EnableStatistics) { actualValue.Statistics.IncrementPacketsReceived(); actualValue.Statistics.AddBytesReceived(size); } switch (packet.Property) { case PacketProperty.ConnectRequest: { NetConnectRequestPacket netConnectRequestPacket = NetConnectRequestPacket.FromData(packet); if (netConnectRequestPacket != null) { ProcessConnectRequest(remoteEndPoint, actualValue, netConnectRequestPacket); } break; } case PacketProperty.PeerNotFound: if (flag) { if (actualValue.ConnectionState == ConnectionState.Connected) { if (packet.Size == 1) { actualValue.ResetMtu(); SendRaw(NetConnectAcceptPacket.MakeNetworkChanged(actualValue), remoteEndPoint); } else if (packet.Size == 2 && packet.RawData[1] == 1) { DisconnectPeerForce(actualValue, DisconnectReason.PeerNotFound, SocketError.Success, null); } } } else { if (packet.Size <= 1) { break; } bool flag2 = false; if (AllowPeerAddressChange) { NetConnectAcceptPacket netConnectAcceptPacket = NetConnectAcceptPacket.FromData(packet); if (netConnectAcceptPacket != null && netConnectAcceptPacket.PeerNetworkChanged && netConnectAcceptPacket.PeerId < _peersArray.Length) { _peersLock.EnterUpgradeableReadLock(); NetPeer netPeer = _peersArray[netConnectAcceptPacket.PeerId]; _peersLock.ExitUpgradeableReadLock(); if (netPeer != null && netPeer.ConnectTime == netConnectAcceptPacket.ConnectionTime && netPeer.ConnectionNum == netConnectAcceptPacket.ConnectionNumber) { if (netPeer.ConnectionState == ConnectionState.Connected) { netPeer.InitiateEndPointChange(); CreateEvent(NetEvent.EType.PeerAddressChanged, netPeer, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } flag2 = true; } } } PoolRecycle(packet); if (!flag2) { NetPacket netPacket = PoolGetWithProperty(PacketProperty.PeerNotFound, 1); netPacket.RawData[1] = 1; SendRawAndRecycle(netPacket, remoteEndPoint); } } break; case PacketProperty.InvalidProtocol: if (flag && actualValue.ConnectionState == ConnectionState.Outgoing) { DisconnectPeerForce(actualValue, DisconnectReason.InvalidProtocol, SocketError.Success, null); } break; case PacketProperty.Disconnect: if (flag) { DisconnectResult disconnectResult = actualValue.ProcessDisconnect(packet); if (disconnectResult == DisconnectResult.None) { PoolRecycle(packet); break; } DisconnectPeerForce(actualValue, (disconnectResult == DisconnectResult.Disconnect) ? DisconnectReason.RemoteConnectionClose : DisconnectReason.ConnectionRejected, SocketError.Success, packet); } else { PoolRecycle(packet); } SendRawAndRecycle(PoolGetWithProperty(PacketProperty.ShutdownOk), remoteEndPoint); break; case PacketProperty.ConnectAccept: if (flag) { NetConnectAcceptPacket netConnectAcceptPacket2 = NetConnectAcceptPacket.FromData(packet); if (netConnectAcceptPacket2 != null && actualValue.ProcessConnectAccept(netConnectAcceptPacket2)) { CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } } break; default: if (flag) { actualValue.ProcessPacket(packet); } else { SendRawAndRecycle(PoolGetWithProperty(PacketProperty.PeerNotFound), remoteEndPoint); } break; } } internal void CreateReceiveEvent(NetPacket packet, DeliveryMethod method, byte channelNumber, int headerSize, NetPeer fromPeer) { if (UnsyncedEvents || UnsyncedReceiveEvent || _manualMode) { NetEvent netEvent; lock (_eventLock) { netEvent = _netEventPoolHead; if (netEvent == null) { netEvent = new NetEvent(this); } else { _netEventPoolHead = netEvent.Next; } } netEvent.Next = null; netEvent.Type = NetEvent.EType.Receive; netEvent.DataReader.SetSource(packet, headerSize); netEvent.Peer = fromPeer; netEvent.DeliveryMethod = method; netEvent.ChannelNumber = channelNumber; ProcessEvent(netEvent); return; } lock (_eventLock) { NetEvent netEvent = _netEventPoolHead; if (netEvent == null) { netEvent = new NetEvent(this); } else { _netEventPoolHead = netEvent.Next; } netEvent.Next = null; netEvent.Type = NetEvent.EType.Receive; netEvent.DataReader.SetSource(packet, headerSize); netEvent.Peer = fromPeer; netEvent.DeliveryMethod = method; netEvent.ChannelNumber = channelNumber; if (_pendingEventTail == null) { _pendingEventHead = netEvent; } else { _pendingEventTail.Next = netEvent; } _pendingEventTail = netEvent; } } public void SendToAll(NetDataWriter writer, DeliveryMethod options) { SendToAll(writer.Data, 0, writer.Length, options); } public void SendToAll(byte[] data, DeliveryMethod options) { SendToAll(data, 0, data.Length, options); } public void SendToAll(byte[] data, int start, int length, DeliveryMethod options) { SendToAll(data, start, length, 0, options); } public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options) { SendToAll(writer.Data, 0, writer.Length, channelNumber, options); } public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options) { SendToAll(data, 0, data.Length, channelNumber, options); } public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options) { try { _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { netPeer.Send(data, start, length, channelNumber, options); } } finally { _peersLock.ExitReadLock(); } } public void SendToAll(NetDataWriter writer, DeliveryMethod options, NetPeer excludePeer) { SendToAll(writer.Data, 0, writer.Length, 0, options, excludePeer); } public void SendToAll(byte[] data, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, 0, data.Length, 0, options, excludePeer); } public void SendToAll(byte[] data, int start, int length, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, start, length, 0, options, excludePeer); } public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { SendToAll(writer.Data, 0, writer.Length, channelNumber, options, excludePeer); } public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, 0, data.Length, channelNumber, options, excludePeer); } public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { try { _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer != excludePeer) { netPeer.Send(data, start, length, channelNumber, options); } } } finally { _peersLock.ExitReadLock(); } } public void SendToAll(ReadOnlySpan data, DeliveryMethod options) { SendToAll(data, 0, options, null); } public void SendToAll(ReadOnlySpan data, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, 0, options, excludePeer); } public void SendToAll(ReadOnlySpan data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { try { _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer != excludePeer) { netPeer.Send(data, channelNumber, options); } } } finally { _peersLock.ExitReadLock(); } } public bool SendUnconnectedMessage(ReadOnlySpan message, IPEndPoint remoteEndPoint) { int headerSize = NetPacket.GetHeaderSize(PacketProperty.UnconnectedMessage); NetPacket netPacket = PoolGetPacket(message.Length + headerSize); netPacket.Property = PacketProperty.UnconnectedMessage; message.CopyTo(new Span(netPacket.RawData, headerSize, message.Length)); return SendRawAndRecycle(netPacket, remoteEndPoint) > 0; } public bool Start() { return Start(0); } public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port) { return Start(addressIPv4, addressIPv6, port, manualMode: false); } public bool Start(string addressIPv4, string addressIPv6, int port) { IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4); IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6); return Start(addressIPv7, addressIPv8, port); } public bool Start(int port) { return Start(IPAddress.Any, IPAddress.IPv6Any, port); } public bool StartInManualMode(IPAddress addressIPv4, IPAddress addressIPv6, int port) { return Start(addressIPv4, addressIPv6, port, manualMode: true); } public bool StartInManualMode(string addressIPv4, string addressIPv6, int port) { IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4); IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6); return StartInManualMode(addressIPv7, addressIPv8, port); } public bool StartInManualMode(int port) { return StartInManualMode(IPAddress.Any, IPAddress.IPv6Any, port); } public bool SendUnconnectedMessage(byte[] message, IPEndPoint remoteEndPoint) { return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint); } public bool SendUnconnectedMessage(NetDataWriter writer, string address, int port) { IPEndPoint remoteEndPoint = NetUtils.MakeEndPoint(address, port); return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint); } public bool SendUnconnectedMessage(NetDataWriter writer, IPEndPoint remoteEndPoint) { return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint); } public bool SendUnconnectedMessage(byte[] message, int start, int length, IPEndPoint remoteEndPoint) { NetPacket packet = PoolGetWithData(PacketProperty.UnconnectedMessage, message, start, length); return SendRawAndRecycle(packet, remoteEndPoint) > 0; } public void TriggerUpdate() { _updateTriggerEvent.Set(); } public void PollEvents(int maxProcessedEvents = 0) { if (_manualMode) { if (_udpSocketv4 != null) { ManualReceive(_udpSocketv4, _bufferEndPointv4, maxProcessedEvents); } if (_udpSocketv6 != null && _udpSocketv6 != _udpSocketv4) { ManualReceive(_udpSocketv6, _bufferEndPointv6, maxProcessedEvents); } } else { if (UnsyncedEvents) { return; } NetEvent netEvent; lock (_eventLock) { netEvent = _pendingEventHead; _pendingEventHead = null; _pendingEventTail = null; } int num = 0; while (netEvent != null) { NetEvent next = netEvent.Next; ProcessEvent(netEvent); netEvent = next; num++; if (num == maxProcessedEvents) { break; } } } } public NetPeer Connect(string address, int port, string key) { return Connect(address, port, NetDataWriter.FromString(key)); } public NetPeer Connect(string address, int port, NetDataWriter connectionData) { IPEndPoint target; try { target = NetUtils.MakeEndPoint(address, port); } catch { CreateEvent(NetEvent.EType.Disconnect, null, null, SocketError.Success, 0, DisconnectReason.UnknownHost, null, DeliveryMethod.Unreliable, 0); return null; } return Connect(target, connectionData); } public NetPeer Connect(IPEndPoint target, string key) { return Connect(target, NetDataWriter.FromString(key)); } public NetPeer Connect(IPEndPoint target, NetDataWriter connectionData) { if (!_isRunning) { throw new InvalidOperationException("Client is not running"); } lock (_requestsDict) { if (_requestsDict.ContainsKey(target)) { return null; } byte connectNum = 0; if (TryGetPeer(target, out var actualValue)) { ConnectionState connectionState = actualValue.ConnectionState; if (connectionState == ConnectionState.Outgoing || connectionState == ConnectionState.Connected) { return actualValue; } connectNum = (byte)((actualValue.ConnectionNum + 1) % 4); RemovePeer(actualValue, enableWriteLock: true); } actualValue = new NetPeer(this, target, GetNextPeerId(), connectNum, connectionData); AddPeer(actualValue); return actualValue; } } public NetPeer Connect(IPEndPoint target, ReadOnlySpan connectionData) { if (!_isRunning) { throw new InvalidOperationException("Client is not running"); } lock (_requestsDict) { if (_requestsDict.ContainsKey(target)) { return null; } byte connectNum = 0; if (TryGetPeer(target, out var actualValue)) { ConnectionState connectionState = actualValue.ConnectionState; if (connectionState == ConnectionState.Outgoing || connectionState == ConnectionState.Connected) { return actualValue; } connectNum = (byte)((actualValue.ConnectionNum + 1) % 4); RemovePeer(actualValue, enableWriteLock: true); } actualValue = new NetPeer(this, target, GetNextPeerId(), connectNum, connectionData); AddPeer(actualValue); return actualValue; } } public void Stop() { Stop(sendDisconnectMessages: true); } public void Stop(bool sendDisconnectMessages) { if (_isRunning) { for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages); } CloseSocket(); _updateTriggerEvent.Set(); if (!_manualMode) { _logicThread.Join(); _logicThread = null; } ClearPeerSet(); _peerIds = new ConcurrentQueue(); _lastPeerId = 0; _connectedPeersCount = 0L; _pendingEventHead = null; _pendingEventTail = null; } } [Conditional("DEBUG")] [Conditional("SIMULATE_NETWORK")] private void ClearPingSimulationList() { lock (_pingSimulationList) { _pingSimulationList.Clear(); } } [Conditional("DEBUG")] [Conditional("SIMULATE_NETWORK")] private void ClearOutboundSimulationList() { lock (_outboundSimulationList) { _outboundSimulationList.Clear(); } } public int GetPeersCount(ConnectionState peerState) { int num = 0; _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if ((netPeer.ConnectionState & peerState) != 0) { num++; } } _peersLock.ExitReadLock(); return num; } public void GetPeersNonAlloc(List peers, ConnectionState peerState) { peers.Clear(); _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if ((netPeer.ConnectionState & peerState) != 0) { peers.Add(netPeer); } } _peersLock.ExitReadLock(); } public void DisconnectAll() { DisconnectAll(null, 0, 0); } public void DisconnectAll(byte[] data, int start, int count) { _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { DisconnectPeer(netPeer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null); } _peersLock.ExitReadLock(); } public void DisconnectPeerForce(NetPeer peer) { DisconnectPeerForce(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, null); } public void DisconnectPeer(NetPeer peer) { DisconnectPeer(peer, null, 0, 0); } public void DisconnectPeer(NetPeer peer, byte[] data) { DisconnectPeer(peer, data, 0, data.Length); } public void DisconnectPeer(NetPeer peer, NetDataWriter writer) { DisconnectPeer(peer, writer.Data, 0, writer.Length); } public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count) { DisconnectPeer(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null); } public void CreateNtpRequest(IPEndPoint endPoint) { _ntpRequests.TryAdd(endPoint, new NtpRequest(endPoint)); } public void CreateNtpRequest(string ntpServerAddress, int port) { IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, port); _ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint)); } public void CreateNtpRequest(string ntpServerAddress) { IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, 123); _ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint)); } public NetPeerEnumerator GetEnumerator() { return new NetPeerEnumerator(_headPeer); } IEnumerator IEnumerable.GetEnumerator() { return new NetPeerEnumerator(_headPeer); } IEnumerator IEnumerable.GetEnumerator() { return new NetPeerEnumerator(_headPeer); } private static int HashSetGetPrime(int min) { int[] primes = Primes; foreach (int num in primes) { if (num >= min) { return num; } } for (int j = min | 1; j < int.MaxValue; j += 2) { if (IsPrime(j) && (j - 1) % 101 != 0) { return j; } } return min; static bool IsPrime(int candidate) { if (((uint)candidate & (true ? 1u : 0u)) != 0) { int num2 = (int)Math.Sqrt(candidate); for (int k = 3; k <= num2; k += 2) { if (candidate % k == 0) { return false; } } return true; } return candidate == 2; } } 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 (item == null) { NetDebug.WriteError($"Contains peer null: {item}"); return false; } if (_buckets != null) { int num = item.GetHashCode() & 0x7FFFFFFF; for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next) { if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(item)) { return true; } } } return false; } public NetPeer GetPeerById(int id) { if (id < 0 || id >= _peersArray.Length) { return null; } return _peersArray[id]; } public bool TryGetPeerById(int id, out NetPeer peer) { peer = GetPeerById(id); return peer != null; } private void AddPeer(NetPeer peer) { if (peer == null) { NetDebug.WriteError($"Add peer null: {peer}"); return; } _peersLock.EnterWriteLock(); if (_headPeer != null) { peer.NextPeer = _headPeer; _headPeer.PrevPeer = peer; } _headPeer = peer; AddPeerToSet(peer); if (peer.Id >= _peersArray.Length) { int num = _peersArray.Length * 2; while (peer.Id >= num) { num *= 2; } Array.Resize(ref _peersArray, num); } _peersArray[peer.Id] = peer; _peersLock.ExitWriteLock(); } private void RemovePeer(NetPeer peer, bool enableWriteLock) { if (enableWriteLock) { _peersLock.EnterWriteLock(); } if (!RemovePeerFromSet(peer)) { if (enableWriteLock) { _peersLock.ExitWriteLock(); } 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); if (enableWriteLock) { _peersLock.ExitWriteLock(); } } private bool RemovePeerFromSet(NetPeer peer) { if (_buckets == null || peer == null) { return false; } int num = peer.GetHashCode() & 0x7FFFFFFF; int num2 = num % _buckets.Length; int num3 = -1; for (int num4 = _buckets[num2] - 1; num4 >= 0; num4 = _slots[num4].Next) { if (_slots[num4].HashCode == num && _slots[num4].Value.Equals(peer)) { if (num3 < 0) { _buckets[num2] = _slots[num4].Next + 1; } else { _slots[num3].Next = _slots[num4].Next; } _slots[num4].HashCode = -1; _slots[num4].Value = null; _slots[num4].Next = _freeList; _count--; if (_count == 0) { _lastIndex = 0; _freeList = -1; } else { _freeList = num4; } return true; } num3 = num4; } return false; } private bool TryGetPeer(IPEndPoint endPoint, out NetPeer actualValue) { if (_buckets != null) { int num = endPoint.GetHashCode() & 0x7FFFFFFF; _peersLock.EnterReadLock(); for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next) { if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(endPoint)) { actualValue = _slots[num2].Value; _peersLock.ExitReadLock(); return true; } } _peersLock.ExitReadLock(); } actualValue = null; return false; } private bool TryGetPeer(SocketAddress saddr, out NetPeer actualValue) { if (_buckets != null) { int num = saddr.GetHashCode() & 0x7FFFFFFF; _peersLock.EnterReadLock(); for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next) { if (_slots[num2].HashCode == num && _slots[num2].Value.Serialize().Equals(saddr)) { actualValue = _slots[num2].Value; _peersLock.ExitReadLock(); return true; } } _peersLock.ExitReadLock(); } actualValue = null; return false; } private bool AddPeerToSet(NetPeer value) { if (_buckets == null) { int num = HashSetGetPrime(0); _buckets = new int[num]; _slots = new Slot[num]; } int num2 = value.GetHashCode() & 0x7FFFFFFF; int num3 = num2 % _buckets.Length; for (int num4 = _buckets[num2 % _buckets.Length] - 1; num4 >= 0; num4 = _slots[num4].Next) { if (_slots[num4].HashCode == num2 && _slots[num4].Value.Equals(value)) { return false; } } int num5; if (_freeList >= 0) { num5 = _freeList; _freeList = _slots[num5].Next; } else { if (_lastIndex == _slots.Length) { int num6 = 2 * _count; num6 = (((uint)num6 > 2147483587u && 2147483587 > _count) ? 2147483587 : HashSetGetPrime(num6)); Slot[] array = new Slot[num6]; Array.Copy(_slots, 0, array, 0, _lastIndex); _buckets = new int[num6]; for (int i = 0; i < _lastIndex; i++) { int num7 = array[i].HashCode % num6; array[i].Next = _buckets[num7] - 1; _buckets[num7] = i + 1; } _slots = array; num3 = num2 % _buckets.Length; } num5 = _lastIndex; _lastIndex++; } _slots[num5].HashCode = num2; _slots[num5].Value = value; _slots[num5].Next = _buckets[num3] - 1; _buckets[num3] = num5 + 1; _count++; return true; } private NetPacket PoolGetWithData(PacketProperty property, byte[] data, int start, int length) { int headerSize = NetPacket.GetHeaderSize(property); NetPacket netPacket = PoolGetPacket(length + headerSize); netPacket.Property = property; Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length); return netPacket; } private NetPacket PoolGetWithProperty(PacketProperty property, int size) { NetPacket netPacket = PoolGetPacket(size + NetPacket.GetHeaderSize(property)); netPacket.Property = property; return netPacket; } private NetPacket PoolGetWithProperty(PacketProperty property) { NetPacket netPacket = PoolGetPacket(NetPacket.GetHeaderSize(property)); netPacket.Property = property; return netPacket; } internal NetPacket PoolGetPacket(int size) { if (size > NetConstants.MaxPacketSize) { return new NetPacket(size); } NetPacket poolHead; lock (_poolLock) { poolHead = _poolHead; if (poolHead == null) { return new NetPacket(size); } _poolHead = _poolHead.Next; _poolCount--; } poolHead.Size = size; if (poolHead.RawData.Length < size) { poolHead.RawData = new byte[size]; } return poolHead; } internal void PoolRecycle(NetPacket packet) { if (packet.RawData.Length > NetConstants.MaxPacketSize || _poolCount >= PacketPoolSize) { return; } packet.RawData[0] = 0; lock (_poolLock) { packet.Next = _poolHead; _poolHead = packet; _poolCount++; } } static NetManager() { Primes = new int[72] { 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 }; MulticastAddressV6 = IPAddress.Parse("ff02::1"); IPv6Support = Socket.OSSupportsIPv6; } private bool ProcessError(SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.NotConnected: NotConnected = true; return true; case SocketError.OperationAborted: case SocketError.Interrupted: case SocketError.NotSocket: return true; default: NetDebug.WriteError($"[R]Error code: {(int)ex.SocketErrorCode} - {ex}"); CreateEvent(NetEvent.EType.Error, null, null, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); break; case SocketError.WouldBlock: case SocketError.MessageSize: case SocketError.NetworkReset: case SocketError.ConnectionReset: case SocketError.TimedOut: break; } return false; } private void ManualReceive(Socket socket, EndPoint bufferEndPoint, int maxReceive) { try { int num = 0; while (socket.Available > 0) { ReceiveFrom(socket, ref bufferEndPoint); num++; if (num == maxReceive) { break; } } } catch (SocketException ex) { ProcessError(ex); } catch (ObjectDisposedException) { } catch (Exception ex3) { NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex3); } } private void NativeReceiveLogic() { IntPtr handle = _udpSocketv4.Handle; IntPtr s2 = _udpSocketv6?.Handle ?? IntPtr.Zero; byte[] address2 = new byte[16]; byte[] address3 = new byte[28]; IPEndPoint tempEndPoint = new IPEndPoint(IPAddress.Any, 0); List list = new List(2); Socket udpSocketv = _udpSocketv4; Socket udpSocketv2 = _udpSocketv6; NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize); while (_isRunning) { try { if (udpSocketv2 == null) { if (!NativeReceiveFrom(handle, address2)) { break; } continue; } bool flag = false; if (udpSocketv.Available != 0 || list.Contains(udpSocketv)) { if (!NativeReceiveFrom(handle, address2)) { break; } flag = true; } if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2)) { if (!NativeReceiveFrom(s2, address3)) { break; } flag = true; } list.Clear(); if (!flag) { list.Add(udpSocketv); list.Add(udpSocketv2); Socket.Select(list, null, null, ReceivePollingTime); } } catch (SocketException ex) { if (ProcessError(ex)) { break; } } catch (ObjectDisposedException) { break; } catch (ThreadAbortException) { break; } catch (Exception ex4) { NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4); } } bool NativeReceiveFrom(IntPtr s, byte[] address) { int socketAddressSize = address.Length; packet.Size = NativeSocket.RecvFrom(s, packet.RawData, NetConstants.MaxPacketSize, address, ref socketAddressSize); if (packet.Size == 0) { return true; } if (packet.Size == -1) { return !ProcessError(new SocketException((int)NativeSocket.GetSocketError())); } short num = (short)((address[1] << 8) | address[0]); tempEndPoint.Port = (ushort)((address[2] << 8) | address[3]); if ((NativeSocket.UnixMode && num == 10) || (!NativeSocket.UnixMode && num == 23)) { uint num2 = (uint)((address[27] << 24) + (address[26] << 16) + (address[25] << 8) + address[24]); tempEndPoint.Address = new IPAddress(new ReadOnlySpan(address, 8, 16), num2); } else { long newAddress = (uint)((address[4] & 0xFF) | ((address[5] << 8) & 0xFF00) | ((address[6] << 16) & 0xFF0000) | (address[7] << 24)); tempEndPoint.Address = new IPAddress(newAddress); } if (TryGetPeer(tempEndPoint, out var actualValue)) { OnMessageReceived(packet, actualValue); } else { OnMessageReceived(packet, tempEndPoint); tempEndPoint = new IPEndPoint(IPAddress.Any, 0); } packet = PoolGetPacket(NetConstants.MaxPacketSize); return true; } } private void ReceiveFrom(Socket s, ref EndPoint bufferEndPoint) { NetPacket netPacket = PoolGetPacket(NetConstants.MaxPacketSize); netPacket.Size = s.ReceiveFrom(netPacket.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, ref bufferEndPoint); OnMessageReceived(netPacket, (IPEndPoint)bufferEndPoint); } private void ReceiveLogic() { EndPoint bufferEndPoint = new IPEndPoint(IPAddress.Any, 0); EndPoint bufferEndPoint2 = new IPEndPoint(IPAddress.IPv6Any, 0); List list = new List(2); Socket udpSocketv = _udpSocketv4; Socket udpSocketv2 = _udpSocketv6; while (_isRunning) { try { if (udpSocketv2 == null) { if (udpSocketv.Available != 0 || udpSocketv.Poll(ReceivePollingTime, SelectMode.SelectRead)) { ReceiveFrom(udpSocketv, ref bufferEndPoint); } continue; } bool flag = false; if (udpSocketv.Available != 0 || list.Contains(udpSocketv)) { ReceiveFrom(udpSocketv, ref bufferEndPoint); flag = true; } if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2)) { ReceiveFrom(udpSocketv2, ref bufferEndPoint2); flag = true; } list.Clear(); if (!flag) { list.Add(udpSocketv); list.Add(udpSocketv2); Socket.Select(list, null, null, ReceivePollingTime); } } catch (SocketException ex) { if (ProcessError(ex)) { break; } } catch (ObjectDisposedException) { break; } catch (ThreadAbortException) { break; } catch (Exception ex4) { NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4); } } } public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool manualMode) { if (IsRunning && !NotConnected) { return false; } NotConnected = false; _manualMode = manualMode; UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported; _udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); if (!BindSocket(_udpSocketv4, new IPEndPoint(addressIPv4, port))) { return false; } LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port; _isRunning = true; if (_manualMode) { _bufferEndPointv4 = new IPEndPoint(IPAddress.Any, 0); } if (IPv6Support && IPv6Enabled) { _udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort))) { if (_manualMode) { _bufferEndPointv6 = new IPEndPoint(IPAddress.IPv6Any, 0); } } else { _udpSocketv6 = null; } } if (!manualMode) { ThreadStart start = ReceiveLogic; if (UseNativeSockets) { start = NativeReceiveLogic; } _receiveThread = new Thread(start) { Name = $"ReceiveThread({LocalPort})", IsBackground = true }; _receiveThread.Start(); if (_logicThread == null) { _logicThread = new Thread(UpdateLogic) { Name = "LogicThread", IsBackground = true }; _logicThread.Start(); } } return true; } private bool BindSocket(Socket socket, IPEndPoint ep) { socket.ReceiveTimeout = 500; socket.SendTimeout = 500; socket.ReceiveBufferSize = 1048576; socket.SendBufferSize = 1048576; socket.Blocking = true; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { try { socket.IOControl(-1744830452, new byte[1], null); } catch { } } try { socket.ExclusiveAddressUse = !ReuseAddress; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, ReuseAddress); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, DontRoute); } catch { } if (ep.AddressFamily == AddressFamily.InterNetwork) { Ttl = 255; try { socket.EnableBroadcast = true; } catch (SocketException ex) { NetDebug.WriteError($"[B]Broadcast error: {ex.SocketErrorCode}"); } if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { try { socket.DontFragment = true; } catch (SocketException ex2) { NetDebug.WriteError($"[B]DontFragment error: {ex2.SocketErrorCode}"); } } } try { socket.Bind(ep); if (ep.AddressFamily == AddressFamily.InterNetworkV6) { try { socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6)); } catch (Exception) { } } } catch (SocketException ex4) { switch (ex4.SocketErrorCode) { case SocketError.AddressAlreadyInUse: if (socket.AddressFamily == AddressFamily.InterNetworkV6) { try { socket.DualMode = false; socket.Bind(ep); } catch (SocketException ex5) { NetDebug.WriteError($"[B]Bind exception: {ex5}, errorCode: {ex5.SocketErrorCode}"); return false; } return true; } break; case SocketError.AddressFamilyNotSupported: return true; } NetDebug.WriteError($"[B]Bind exception: {ex4}, errorCode: {ex4.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 netPacket = null; if (_extraPacketLayer != null) { netPacket = PoolGetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer); Buffer.BlockCopy(message, start, netPacket.RawData, 0, length); start = 0; _extraPacketLayer.ProcessOutBoundPacket(ref remoteEndPoint, ref netPacket.RawData, ref start, ref length); message = netPacket.RawData; } return SendRawCoreWithCleanup(message, start, length, remoteEndPoint, netPacket); } private int SendRawCoreWithCleanup(byte[] message, int start, int length, IPEndPoint remoteEndPoint, NetPacket expandedPacket) { try { return SendRawCore(message, start, length, remoteEndPoint); } finally { if (expandedPacket != null) { PoolRecycle(expandedPacket); } } } internal unsafe int SendRawCore(byte[] message, int start, int length, IPEndPoint remoteEndPoint) { if (!_isRunning) { return 0; } Socket socket = _udpSocketv4; if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) { socket = _udpSocketv6; if (socket == null) { return 0; } } int num; try { if (UseNativeSockets && remoteEndPoint is NetPeer netPeer) { fixed (byte* pinnedBuffer = &message[start]) { num = NativeSocket.SendTo(socket.Handle, pinnedBuffer, length, netPeer.NativeAddress, netPeer.NativeAddress.Length); } if (num == -1) { throw NativeSocket.GetSocketException(); } } else { num = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint); } } catch (SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.Interrupted: case SocketError.NoBufferSpaceAvailable: return 0; case SocketError.MessageSize: return 0; case SocketError.NetworkUnreachable: case SocketError.HostUnreachable: if (DisconnectOnUnreachable && remoteEndPoint is NetPeer peer) { DisconnectPeerForce(peer, (ex.SocketErrorCode == SocketError.HostUnreachable) ? DisconnectReason.HostUnreachable : DisconnectReason.NetworkUnreachable, ex.SocketErrorCode, null); } CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); return -1; case SocketError.Shutdown: CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); return -1; default: NetDebug.WriteError($"[S] {ex}"); return -1; } } catch (Exception arg) { NetDebug.WriteError($"[S] {arg}"); return 0; } if (num <= 0) { return 0; } if (EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(length); } return num; } 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 netPacket; if (_extraPacketLayer != null) { int headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast); netPacket = PoolGetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer); netPacket.Property = PacketProperty.Broadcast; Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length); int offset = 0; int length2 = length + headerSize; IPEndPoint endPoint = null; _extraPacketLayer.ProcessOutBoundPacket(ref endPoint, ref netPacket.RawData, ref offset, ref length2); } else { netPacket = PoolGetWithData(PacketProperty.Broadcast, data, start, length); } bool flag = false; bool flag2 = false; try { flag = _udpSocketv4.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port)) > 0; if (_udpSocketv6 != null) { flag2 = _udpSocketv6.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port)) > 0; } } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.HostUnreachable) { return flag; } NetDebug.WriteError($"[S][MCAST] {ex}"); return flag; } catch (Exception arg) { NetDebug.WriteError($"[S][MCAST] {arg}"); return flag; } finally { PoolRecycle(netPacket); } return flag || flag2; } private void CloseSocket() { _isRunning = false; if (_receiveThread != null && _receiveThread != Thread.CurrentThread) { _receiveThread.Join(); } _receiveThread = null; _udpSocketv4?.Close(); _udpSocketv6?.Close(); _udpSocketv4 = null; _udpSocketv6 = null; } } 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; private static readonly int[] HeaderSizes; public byte[] RawData; public int Size; public object UserData; public NetPacket Next; public PacketProperty Property { get { return (PacketProperty)(RawData[0] & 0x1Fu); } set { RawData[0] = (byte)((RawData[0] & 0xE0u) | (uint)value); } } public byte ConnectionNumber { get { return (byte)((RawData[0] & 0x60) >> 5); } set { RawData[0] = (byte)((RawData[0] & 0x9Fu) | (uint)(value << 5)); } } public ushort Sequence { get { return BitConverter.ToUInt16(RawData, 1); } set { FastBitConverter.GetBytes(RawData, 1, value); } } public bool IsFragmented => (RawData[0] & 0x80) != 0; public byte ChannelId { get { return RawData[3]; } set { RawData[3] = value; } } public ushort FragmentId { get { return BitConverter.ToUInt16(RawData, 4); } set { FastBitConverter.GetBytes(RawData, 4, value); } } public ushort FragmentPart { get { return BitConverter.ToUInt16(RawData, 6); } set { FastBitConverter.GetBytes(RawData, 6, value); } } public ushort FragmentsTotal { get { return BitConverter.ToUInt16(RawData, 8); } set { FastBitConverter.GetBytes(RawData, 8, value); } } static NetPacket() { PropertiesCount = Enum.GetValues(typeof(PacketProperty)).Length; HeaderSizes = NetUtils.AllocatePinnedUninitializedArray(PropertiesCount); for (int i = 0; i < HeaderSizes.Length; i++) { switch ((PacketProperty)(byte)i) { case PacketProperty.Channeled: case PacketProperty.Ack: HeaderSizes[i] = 4; break; case PacketProperty.Ping: HeaderSizes[i] = 3; break; case PacketProperty.ConnectRequest: HeaderSizes[i] = 18; break; case PacketProperty.ConnectAccept: HeaderSizes[i] = 15; break; case PacketProperty.Disconnect: HeaderSizes[i] = 9; break; case PacketProperty.Pong: HeaderSizes[i] = 11; break; default: HeaderSizes[i] = 1; break; } } } public void MarkFragmented() { RawData[0] |= 128; } 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[(uint)property]; } public int GetHeaderSize() { return HeaderSizes[RawData[0] & 0x1F]; } public bool Verify() { byte b = (byte)(RawData[0] & 0x1Fu); if (b >= PropertiesCount) { return false; } int num = HeaderSizes[b]; bool flag = (RawData[0] & 0x80) != 0; if (Size >= num) { if (flag) { return Size >= num + 6; } return true; } return false; } public static implicit operator Span(NetPacket p) { return new Span(p.RawData, 0, p.Size); } } [Flags] public enum ConnectionState : byte { Outgoing = 2, Connected = 4, ShutdownRequested = 8, Disconnected = 0x10, EndPointChange = 0x20, Any = 0x2E } internal enum ConnectRequestResult { None, P2PLose, Reconnection, NewConnection } internal enum DisconnectResult { None, Reject, Disconnect } internal enum ShutdownResult { None, Success, WasConnected } public class NetPeer : IPEndPoint { private class IncomingFragments { public NetPacket[] Fragments; public int ReceivedCount; public int TotalSize; public byte ChannelId; } private int _rtt; private int _avgRtt; private int _rttCount; private double _resendDelay = 27.0; private float _pingSendTimer; private float _rttResetTimer; private readonly Stopwatch _pingTimer = new Stopwatch(); private volatile float _timeSinceLastPacket; private long _remoteDelta; private readonly object _shutdownLock = new object(); internal volatile NetPeer NextPeer; internal NetPeer PrevPeer; private NetPacket[] _unreliableSecondQueue; private NetPacket[] _unreliableChannel; private int _unreliablePendingCount; private readonly object _unreliableChannelLock = new object(); private readonly ConcurrentQueue _channelSendQueue; private readonly BaseChannel[] _channels; private int _mtu; private int _mtuIdx; private bool _finishMtu; private float _mtuCheckTimer; private int _mtuCheckAttempts; private const int MtuCheckDelay = 1000; private const int MaxMtuCheckAttempts = 4; private readonly object _mtuMutex = new object(); private int _fragmentId; private readonly Dictionary _holdedFragments; private readonly Dictionary _deliveredFragments; private readonly NetPacket _mergeData; private int _mergePos; private int _mergeCount; private int _connectAttempts; private float _connectTimer; private long _connectTime; private byte _connectNum; private ConnectionState _connectionState; private NetPacket _shutdownPacket; private const int ShutdownDelay = 300; private float _shutdownTimer; private readonly NetPacket _pingPacket; private readonly NetPacket _pongPacket; private readonly NetPacket _connectRequestPacket; private readonly NetPacket _connectAcceptPacket; public readonly NetManager NetManager; public readonly int Id; public object Tag; public readonly NetStatistics Statistics; private SocketAddress _cachedSocketAddr; private int _cachedHashCode; internal byte[] NativeAddress; internal byte ConnectionNum { get { return _connectNum; } private set { _connectNum = value; _mergeData.ConnectionNumber = value; _pingPacket.ConnectionNumber = value; _pongPacket.ConnectionNumber = value; } } public ConnectionState ConnectionState => _connectionState; internal long ConnectTime => _connectTime; public int RemoteId { get; private set; } public int Ping => _avgRtt / 2; public int RoundTripTime => _avgRtt; public int Mtu => _mtu; public long RemoteTimeDelta => _remoteDelta; public DateTime RemoteUtcTime => new DateTime(DateTime.UtcNow.Ticks + _remoteDelta); public float TimeSinceLastPacket => _timeSinceLastPacket; internal double ResendDelay => _resendDelay; public override SocketAddress Serialize() { return _cachedSocketAddr; } public override int GetHashCode() { return _cachedHashCode; } internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id) : base(remoteEndPoint.Address, remoteEndPoint.Port) { Id = id; Statistics = new NetStatistics(); NetManager = netManager; _cachedSocketAddr = base.Serialize(); if (NetManager.UseNativeSockets) { NativeAddress = new byte[_cachedSocketAddr.Size]; for (int i = 0; i < _cachedSocketAddr.Size; i++) { NativeAddress[i] = _cachedSocketAddr[i]; } } _cachedHashCode = base.GetHashCode(); ResetMtu(); _connectionState = ConnectionState.Connected; _mergeData = new NetPacket(PacketProperty.Merged, NetConstants.MaxPacketSize); _pongPacket = new NetPacket(PacketProperty.Pong, 0); _pingPacket = new NetPacket(PacketProperty.Ping, 0) { Sequence = 1 }; _unreliableSecondQueue = new NetPacket[8]; _unreliableChannel = new NetPacket[8]; _holdedFragments = new Dictionary(); _deliveredFragments = new Dictionary(); _channels = new BaseChannel[netManager.ChannelsCount * 4]; _channelSendQueue = new ConcurrentQueue(); } internal void InitiateEndPointChange() { ResetMtu(); _connectionState = ConnectionState.EndPointChange; } internal void FinishEndPointChange(IPEndPoint newEndPoint) { if (_connectionState != ConnectionState.EndPointChange) { return; } _connectionState = ConnectionState.Connected; base.Address = newEndPoint.Address; base.Port = newEndPoint.Port; _cachedSocketAddr = base.Serialize(); if (NetManager.UseNativeSockets) { NativeAddress = new byte[_cachedSocketAddr.Size]; for (int i = 0; i < _cachedSocketAddr.Size; i++) { NativeAddress[i] = _cachedSocketAddr[i]; } } _cachedHashCode = base.GetHashCode(); } internal void ResetMtu() { _finishMtu = !NetManager.MtuDiscovery; if (NetManager.MtuOverride > 0) { OverrideMtu(NetManager.MtuOverride); } else { SetMtu(0); } } private void SetMtu(int mtuIdx) { _mtuIdx = mtuIdx; _mtu = NetConstants.PossibleMtu[mtuIdx] - NetManager.ExtraPacketSizeForLayer; } private void OverrideMtu(int mtuValue) { _mtu = mtuValue; _finishMtu = true; } public int GetPacketsCountInReliableQueue(byte channelNumber, bool ordered) { int num = channelNumber * 4 + (ordered ? 2 : 0); BaseChannel baseChannel = _channels[num]; if (baseChannel == null) { return 0; } return ((ReliableChannel)baseChannel).PacketsInQueue; } public PooledPacket CreatePacketFromPool(DeliveryMethod deliveryMethod, byte channelNumber) { int mtu = _mtu; NetPacket netPacket = NetManager.PoolGetPacket(mtu); if (deliveryMethod == DeliveryMethod.Unreliable) { netPacket.Property = PacketProperty.Unreliable; return new PooledPacket(netPacket, mtu, 0); } netPacket.Property = PacketProperty.Channeled; return new PooledPacket(netPacket, mtu, (byte)((uint)(channelNumber * 4) + (uint)deliveryMethod)); } public void SendPooledPacket(PooledPacket packet, int userDataSize) { if (_connectionState == ConnectionState.Connected) { packet._packet.Size = packet.UserDataOffset + userDataSize; if (packet._packet.Property == PacketProperty.Channeled) { CreateChannel(packet._channelNumber).AddToQueue(packet._packet); } else { EnqueueUnreliable(packet._packet); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnqueueUnreliable(NetPacket packet) { lock (_unreliableChannelLock) { if (_unreliablePendingCount == _unreliableChannel.Length) { Array.Resize(ref _unreliableChannel, _unreliablePendingCount * 2); } _unreliableChannel[_unreliablePendingCount++] = packet; } } private BaseChannel CreateChannel(byte idx) { BaseChannel baseChannel = _channels[idx]; if (baseChannel != null) { return baseChannel; } switch ((DeliveryMethod)(byte)(idx % 4)) { case DeliveryMethod.ReliableUnordered: baseChannel = new ReliableChannel(this, ordered: false, idx); break; case DeliveryMethod.Sequenced: baseChannel = new SequencedChannel(this, reliable: false, idx); break; case DeliveryMethod.ReliableOrdered: baseChannel = new ReliableChannel(this, ordered: true, idx); break; case DeliveryMethod.ReliableSequenced: baseChannel = new SequencedChannel(this, reliable: true, idx); break; } BaseChannel baseChannel2 = Interlocked.CompareExchange(ref _channels[idx], baseChannel, null); if (baseChannel2 != null) { return baseChannel2; } return baseChannel; } internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id, byte connectNum, NetDataWriter connectData) : this(netManager, remoteEndPoint, id) { _connectTime = DateTime.UtcNow.Ticks; _connectionState = ConnectionState.Outgoing; ConnectionNum = connectNum; _connectRequestPacket = NetConnectRequestPacket.Make(connectData, remoteEndPoint.Serialize(), _connectTime, id); _connectRequestPacket.ConnectionNumber = connectNum; NetManager.SendRaw(_connectRequestPacket, this); } internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id, byte connectNum, ReadOnlySpan connectData) : this(netManager, remoteEndPoint, id) { _connectTime = DateTime.UtcNow.Ticks; _connectionState = ConnectionState.Outgoing; ConnectionNum = connectNum; _connectRequestPacket = NetConnectRequestPacket.Make(connectData, remoteEndPoint.Serialize(), _connectTime, id); _connectRequestPacket.ConnectionNumber = connectNum; NetManager.SendRaw(_connectRequestPacket, this); } internal NetPeer(NetManager netManager, ConnectionRequest request, int id) : this(netManager, request.RemoteEndPoint, id) { _connectTime = request.InternalPacket.ConnectionTime; ConnectionNum = request.InternalPacket.ConnectionNumber; RemoteId = request.InternalPacket.PeerId; _connectAcceptPacket = NetConnectAcceptPacket.Make(_connectTime, ConnectionNum, id); _connectionState = ConnectionState.Connected; NetManager.SendRaw(_connectAcceptPacket, this); } internal void Reject(NetConnectRequestPacket requestData, byte[] data, int start, int length) { _connectTime = requestData.ConnectionTime; _connectNum = requestData.ConnectionNumber; Shutdown(data, start, length, force: false); } internal bool ProcessConnectAccept(NetConnectAcceptPacket packet) { if (_connectionState != ConnectionState.Outgoing) { return false; } if (packet.ConnectionTime != _connectTime) { return false; } ConnectionNum = packet.ConnectionNumber; RemoteId = packet.PeerId; Interlocked.Exchange(ref _timeSinceLastPacket, 0f); _connectionState = ConnectionState.Connected; return true; } public int GetMaxSinglePacketSize(DeliveryMethod options) { return _mtu - NetPacket.GetHeaderSize((options != DeliveryMethod.Unreliable) ? PacketProperty.Channeled : PacketProperty.Unreliable); } public void SendWithDeliveryEvent(byte[] data, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != 0) { throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); } SendInternal(data, 0, data.Length, channelNumber, deliveryMethod, userData); } public void SendWithDeliveryEvent(byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != 0) { throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); } SendInternal(data, start, length, channelNumber, deliveryMethod, userData); } public void SendWithDeliveryEvent(NetDataWriter dataWriter, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != 0) { throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); } SendInternal(dataWriter.Data, 0, dataWriter.Length, channelNumber, deliveryMethod, userData); } public void Send(byte[] data, DeliveryMethod deliveryMethod) { SendInternal(data, 0, data.Length, 0, deliveryMethod, null); } public void Send(NetDataWriter dataWriter, DeliveryMethod deliveryMethod) { SendInternal(dataWriter.Data, 0, dataWriter.Length, 0, deliveryMethod, null); } public void Send(byte[] data, int start, int length, DeliveryMethod options) { SendInternal(data, start, length, 0, options, null); } public void Send(byte[] data, byte channelNumber, DeliveryMethod deliveryMethod) { SendInternal(data, 0, data.Length, channelNumber, deliveryMethod, null); } public void Send(NetDataWriter dataWriter, byte channelNumber, DeliveryMethod deliveryMethod) { SendInternal(dataWriter.Data, 0, dataWriter.Length, channelNumber, deliveryMethod, null); } public void Send(byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod) { SendInternal(data, start, length, channelNumber, deliveryMethod, null); } private void SendInternal(byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (_connectionState != ConnectionState.Connected || channelNumber >= _channels.Length) { return; } BaseChannel baseChannel = null; PacketProperty property; if (deliveryMethod == DeliveryMethod.Unreliable) { property = PacketProperty.Unreliable; } else { property = PacketProperty.Channeled; baseChannel = CreateChannel((byte)((uint)(channelNumber * 4) + (uint)deliveryMethod)); } int headerSize = NetPacket.GetHeaderSize(property); int mtu = _mtu; if (length + headerSize > mtu) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != 0) { throw new TooBigPacketException("Unreliable or ReliableSequenced packet size exceeded maximum of " + (mtu - headerSize) + " bytes, Check allowed size by GetMaxSinglePacketSize()"); } int num = mtu - headerSize - 6; int num2 = length / num + ((length % num != 0) ? 1 : 0); if (num2 > 65535) { throw new TooBigPacketException("Data was split in " + num2 + " fragments, which exceeds " + ushort.MaxValue); } ushort fragmentId = (ushort)Interlocked.Increment(ref _fragmentId); for (ushort num3 = 0; num3 < num2; num3++) { int num4 = ((length > num) ? num : length); NetPacket netPacket = NetManager.PoolGetPacket(headerSize + num4 + 6); netPacket.Property = property; netPacket.UserData = userData; netPacket.FragmentId = fragmentId; netPacket.FragmentPart = num3; netPacket.FragmentsTotal = (ushort)num2; netPacket.MarkFragmented(); Buffer.BlockCopy(data, start + num3 * num, netPacket.RawData, 10, num4); baseChannel.AddToQueue(netPacket); length -= num4; } } else { NetPacket netPacket2 = NetManager.PoolGetPacket(headerSize + length); netPacket2.Property = property; Buffer.BlockCopy(data, start, netPacket2.RawData, headerSize, length); netPacket2.UserData = userData; if (baseChannel == null) { EnqueueUnreliable(netPacket2); } else { baseChannel.AddToQueue(netPacket2); } } } public void SendWithDeliveryEvent(ReadOnlySpan data, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != 0) { throw new ArgumentException("Delivery event will work only for ReliableOrdered/Unordered packets"); } SendInternal(data, channelNumber, deliveryMethod, userData); } public void Send(ReadOnlySpan data, DeliveryMethod deliveryMethod) { SendInternal(data, 0, deliveryMethod, null); } public void Send(ReadOnlySpan data, byte channelNumber, DeliveryMethod deliveryMethod) { SendInternal(data, channelNumber, deliveryMethod, null); } private void SendInternal(ReadOnlySpan data, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (_connectionState != ConnectionState.Connected || channelNumber >= _channels.Length) { return; } BaseChannel baseChannel = null; PacketProperty property; if (deliveryMethod == DeliveryMethod.Unreliable) { property = PacketProperty.Unreliable; } else { property = PacketProperty.Channeled; baseChannel = CreateChannel((byte)((uint)(channelNumber * 4) + (uint)deliveryMethod)); } int headerSize = NetPacket.GetHeaderSize(property); int mtu = _mtu; int num = data.Length; if (num + headerSize > mtu) { if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != 0) { throw new TooBigPacketException("Unreliable or ReliableSequenced packet size exceeded maximum of " + (mtu - headerSize) + " bytes, Check allowed size by GetMaxSinglePacketSize()"); } int num2 = mtu - headerSize - 6; int num3 = num / num2 + ((num % num2 != 0) ? 1 : 0); if (num3 > 65535) { throw new TooBigPacketException("Data was split in " + num3 + " fragments, which exceeds " + ushort.MaxValue); } ushort fragmentId = (ushort)Interlocked.Increment(ref _fragmentId); for (ushort num4 = 0; num4 < num3; num4++) { int num5 = ((num > num2) ? num2 : num); NetPacket netPacket = NetManager.PoolGetPacket(headerSize + num5 + 6); netPacket.Property = property; netPacket.UserData = userData; netPacket.FragmentId = fragmentId; netPacket.FragmentPart = num4; netPacket.FragmentsTotal = (ushort)num3; netPacket.MarkFragmented(); data.Slice(num4 * num2, num5).CopyTo(new Span(netPacket.RawData, 10, num5)); baseChannel.AddToQueue(netPacket); num -= num5; } } else { NetPacket netPacket2 = NetManager.PoolGetPacket(headerSize + num); netPacket2.Property = property; data.CopyTo(new Span(netPacket2.RawData, headerSize, num)); netPacket2.UserData = userData; if (baseChannel == null) { EnqueueUnreliable(netPacket2); } else { baseChannel.AddToQueue(netPacket2); } } } public void Disconnect(byte[] data) { NetManager.DisconnectPeer(this, data); } public void Disconnect(NetDataWriter writer) { NetManager.DisconnectPeer(this, writer); } public void Disconnect(byte[] data, int start, int count) { NetManager.DisconnectPeer(this, data, start, count); } public void Disconnect() { NetManager.DisconnectPeer(this); } internal DisconnectResult ProcessDisconnect(NetPacket packet) { if ((_connectionState == ConnectionState.Connected || _connectionState == ConnectionState.Outgoing) && packet.Size >= 9 && BitConverter.ToInt64(packet.RawData, 1) == _connectTime && packet.ConnectionNumber == _connectNum) { if (_connectionState != ConnectionState.Connected) { return DisconnectResult.Reject; } return DisconnectResult.Disconnect; } return DisconnectResult.None; } internal void AddToReliableChannelSendQueue(BaseChannel channel) { _channelSendQueue.Enqueue(channel); } internal ShutdownResult Shutdown(byte[] data, int start, int length, bool force) { lock (_shutdownLock) { if (_connectionState == ConnectionState.Disconnected || _connectionState == ConnectionState.ShutdownRequested) { return ShutdownResult.None; } ShutdownResult result = ((_connectionState != ConnectionState.Connected) ? ShutdownResult.Success : ShutdownResult.WasConnected); if (force) { _connectionState = ConnectionState.Disconnected; return result; } Interlocked.Exchange(ref _timeSinceLastPacket, 0f); _shutdownPacket = new NetPacket(PacketProperty.Disconnect, length) { ConnectionNumber = _connectNum }; FastBitConverter.GetBytes(_shutdownPacket.RawData, 1, _connectTime); if (_shutdownPacket.Size >= _mtu) { NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU - 8!"); } else if (data != null && length > 0) { Buffer.BlockCopy(data, start, _shutdownPacket.RawData, 9, length); } _connectionState = ConnectionState.ShutdownRequested; NetManager.SendRaw(_shutdownPacket, this); return result; } } private void UpdateRoundTripTime(int roundTripTime) { _rtt += roundTripTime; _rttCount++; _avgRtt = _rtt / _rttCount; _resendDelay = 25.0 + (double)_avgRtt * 2.1; } internal void AddReliablePacket(DeliveryMethod method, NetPacket p) { if (p.IsFragmented) { ushort fragmentId = p.FragmentId; byte channelId = p.ChannelId; if (!_holdedFragments.TryGetValue(fragmentId, out var value)) { if (_holdedFragments.Count >= 32 * NetManager.ChannelsCount * 2) { NetManager.PoolRecycle(p); return; } value = new IncomingFragments { Fragments = new NetPacket[p.FragmentsTotal], ChannelId = p.ChannelId }; _holdedFragments.Add(fragmentId, value); } NetPacket[] fragments = value.Fragments; if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null || p.ChannelId != value.ChannelId) { NetManager.PoolRecycle(p); NetDebug.WriteError("Invalid fragment packet"); return; } fragments[p.FragmentPart] = p; value.ReceivedCount++; value.TotalSize += p.Size - 10; if (value.ReceivedCount != fragments.Length) { return; } NetPacket netPacket = NetManager.PoolGetPacket(value.TotalSize); int num = 0; for (int i = 0; i < value.ReceivedCount; i++) { NetPacket netPacket2 = fragments[i]; int num2 = netPacket2.Size - 10; if (num + num2 > netPacket.RawData.Length) { _holdedFragments.Remove(fragmentId); NetDebug.WriteError($"Fragment error pos: {num + num2} >= resultPacketSize: {netPacket.RawData.Length} , totalSize: {value.TotalSize}"); return; } if (netPacket2.Size > netPacket2.RawData.Length) { _holdedFragments.Remove(fragmentId); NetDebug.WriteError($"Fragment error size: {netPacket2.Size} > fragment.RawData.Length: {netPacket2.RawData.Length}"); return; } Buffer.BlockCopy(netPacket2.RawData, 10, netPacket.RawData, num, num2); num += num2; NetManager.PoolRecycle(netPacket2); fragments[i] = null; } _holdedFragments.Remove(fragmentId); NetManager.CreateReceiveEvent(netPacket, method, (byte)(channelId / 4), 0, this); } else { NetManager.CreateReceiveEvent(p, method, (byte)(p.ChannelId / 4), 4, this); } } private void ProcessMtuPacket(NetPacket packet) { if (packet.Size < NetConstants.PossibleMtu[0]) { return; } int num = BitConverter.ToInt32(packet.RawData, 1); int num2 = BitConverter.ToInt32(packet.RawData, packet.Size - 4); if (num != packet.Size || num != num2 || num > NetConstants.MaxPacketSize) { NetDebug.WriteError($"[MTU] Broken packet. RMTU {num}, EMTU {num2}, PSIZE {packet.Size}"); } else if (packet.Property == PacketProperty.MtuCheck) { _mtuCheckAttempts = 0; packet.Property = PacketProperty.MtuOk; NetManager.SendRawAndRecycle(packet, this); } else if (num > _mtu && !_finishMtu && num == NetConstants.PossibleMtu[_mtuIdx + 1] - NetManager.ExtraPacketSizeForLayer) { lock (_mtuMutex) { SetMtu(_mtuIdx + 1); } if (_mtuIdx == NetConstants.PossibleMtu.Length - 1) { _finishMtu = true; } NetManager.PoolRecycle(packet); } } private void UpdateMtuLogic(float deltaTime) { if (_finishMtu) { return; } _mtuCheckTimer += deltaTime; if (_mtuCheckTimer < 1000f) { return; } _mtuCheckTimer = 0f; _mtuCheckAttempts++; if (_mtuCheckAttempts >= 4) { _finishMtu = true; return; } lock (_mtuMutex) { if (_mtuIdx < NetConstants.PossibleMtu.Length - 1) { int num = NetConstants.PossibleMtu[_mtuIdx + 1] - NetManager.ExtraPacketSizeForLayer; NetPacket netPacket = NetManager.PoolGetPacket(num); netPacket.Property = PacketProperty.MtuCheck; FastBitConverter.GetBytes(netPacket.RawData, 1, num); FastBitConverter.GetBytes(netPacket.RawData, netPacket.Size - 4, num); if (NetManager.SendRawAndRecycle(netPacket, this) <= 0) { _finishMtu = true; } } } } internal ConnectRequestResult ProcessConnectRequest(NetConnectRequestPacket connRequest) { switch (_connectionState) { case ConnectionState.Outgoing: { if (connRequest.ConnectionTime < _connectTime) { return ConnectRequestResult.P2PLose; } if (connRequest.ConnectionTime != _connectTime) { break; } byte[] targetAddress = connRequest.TargetAddress; for (int num = _cachedSocketAddr.Size - 1; num >= 0; num--) { byte b = _cachedSocketAddr[num]; if (b != targetAddress[num] && b < targetAddress[num]) { return ConnectRequestResult.P2PLose; } } break; } case ConnectionState.Connected: if (connRequest.ConnectionTime == _connectTime) { NetManager.SendRaw(_connectAcceptPacket, this); } else if (connRequest.ConnectionTime > _connectTime) { return ConnectRequestResult.Reconnection; } break; case ConnectionState.ShutdownRequested: case ConnectionState.Disconnected: if (connRequest.ConnectionTime >= _connectTime) { return ConnectRequestResult.NewConnection; } break; } return ConnectRequestResult.None; } internal void ProcessPacket(NetPacket packet) { if (_connectionState == ConnectionState.Outgoing || _connectionState == ConnectionState.Disconnected) { NetManager.PoolRecycle(packet); return; } if (packet.Property == PacketProperty.ShutdownOk) { if (_connectionState == ConnectionState.ShutdownRequested) { _connectionState = ConnectionState.Disconnected; } NetManager.PoolRecycle(packet); return; } if (packet.ConnectionNumber != _connectNum) { NetManager.PoolRecycle(packet); return; } Interlocked.Exchange(ref _timeSinceLastPacket, 0f); switch (packet.Property) { case PacketProperty.Merged: { int num2 = 1; while (num2 < packet.Size) { ushort num3 = BitConverter.ToUInt16(packet.RawData, num2); if (num3 == 0) { break; } num2 += 2; if (packet.RawData.Length - num2 < num3) { break; } NetPacket netPacket = NetManager.PoolGetPacket(num3); Buffer.BlockCopy(packet.RawData, num2, netPacket.RawData, 0, num3); netPacket.Size = num3; if (!netPacket.Verify()) { break; } num2 += num3; ProcessPacket(netPacket); } NetManager.PoolRecycle(packet); break; } case PacketProperty.Ping: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pongPacket.Sequence) > 0) { FastBitConverter.GetBytes(_pongPacket.RawData, 3, DateTime.UtcNow.Ticks); _pongPacket.Sequence = packet.Sequence; NetManager.SendRaw(_pongPacket, this); } NetManager.PoolRecycle(packet); break; case PacketProperty.Pong: if (packet.Sequence == _pingPacket.Sequence) { _pingTimer.Stop(); int num = (int)_pingTimer.ElapsedMilliseconds; _remoteDelta = BitConverter.ToInt64(packet.RawData, 3) + (long)num * 10000L / 2 - DateTime.UtcNow.Ticks; UpdateRoundTripTime(num); NetManager.ConnectionLatencyUpdated(this, num / 2); } NetManager.PoolRecycle(packet); break; case PacketProperty.Channeled: case PacketProperty.Ack: { if (packet.ChannelId >= _channels.Length) { NetManager.PoolRecycle(packet); break; } BaseChannel baseChannel = _channels[packet.ChannelId] ?? ((packet.Property == PacketProperty.Ack) ? null : CreateChannel(packet.ChannelId)); if (baseChannel != null && !baseChannel.ProcessPacket(packet)) { NetManager.PoolRecycle(packet); } break; } case PacketProperty.Unreliable: NetManager.CreateReceiveEvent(packet, DeliveryMethod.Unreliable, 0, 1, this); break; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; default: NetDebug.WriteError("Error! Unexpected packet type: " + packet.Property); break; } } private void SendMerged() { if (_mergeCount != 0) { int num = ((_mergeCount <= 1) ? NetManager.SendRaw(_mergeData.RawData, 3, _mergePos - 2, this) : NetManager.SendRaw(_mergeData.RawData, 0, 1 + _mergePos, this)); if (NetManager.EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(num); } _mergePos = 0; _mergeCount = 0; } } internal void SendUserData(NetPacket packet) { packet.ConnectionNumber = _connectNum; int num = 1 + packet.Size + 2; if (num + 20 >= _mtu) { int num2 = NetManager.SendRaw(packet, this); if (NetManager.EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(num2); } return; } if (_mergePos + num > _mtu) { SendMerged(); } FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + 1, (ushort)packet.Size); Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + 1 + 2, packet.Size); _mergePos += packet.Size + 2; _mergeCount++; } internal void Update(float deltaTime) { _timeSinceLastPacket += deltaTime; switch (_connectionState) { case ConnectionState.Connected: if (_timeSinceLastPacket > (float)NetManager.DisconnectTimeout) { NetManager.DisconnectPeerForce(this, DisconnectReason.Timeout, SocketError.Success, null); return; } break; case ConnectionState.ShutdownRequested: if (_timeSinceLastPacket > (float)NetManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; return; } _shutdownTimer += deltaTime; if (_shutdownTimer >= 300f) { _shutdownTimer = 0f; NetManager.SendRaw(_shutdownPacket, this); } return; case ConnectionState.Outgoing: _connectTimer += deltaTime; if (_connectTimer > (float)NetManager.ReconnectDelay) { _connectTimer = 0f; _connectAttempts++; if (_connectAttempts > NetManager.MaxConnectAttempts) { NetManager.DisconnectPeerForce(this, DisconnectReason.ConnectionFailed, SocketError.Success, null); } else { NetManager.SendRaw(_connectRequestPacket, this); } } return; case ConnectionState.Disconnected: return; } _pingSendTimer += deltaTime; if (_pingSendTimer >= (float)NetManager.PingInterval) { _pingSendTimer = 0f; _pingPacket.Sequence++; if (_pingTimer.IsRunning) { UpdateRoundTripTime((int)_pingTimer.ElapsedMilliseconds); } _pingTimer.Restart(); NetManager.SendRaw(_pingPacket, this); } _rttResetTimer += deltaTime; if (_rttResetTimer >= (float)(NetManager.PingInterval * 3)) { _rttResetTimer = 0f; _rtt = _avgRtt; _rttCount = 1; } UpdateMtuLogic(deltaTime); int count = _channelSendQueue.Count; BaseChannel result; while (count-- > 0 && _channelSendQueue.TryDequeue(out result)) { if (result.SendAndCheckQueue()) { _channelSendQueue.Enqueue(result); } } if (_unreliablePendingCount > 0) { int unreliablePendingCount; lock (_unreliableChannelLock) { NetPacket[] unreliableSecondQueue = _unreliableSecondQueue; NetPacket[] unreliableChannel = _unreliableChannel; _unreliableChannel = unreliableSecondQueue; _unreliableSecondQueue = unreliableChannel; unreliablePendingCount = _unreliablePendingCount; _unreliablePendingCount = 0; } for (int i = 0; i < unreliablePendingCount; i++) { NetPacket packet = _unreliableSecondQueue[i]; SendUserData(packet); NetManager.PoolRecycle(packet); } } SendMerged(); } internal void RecycleAndDeliver(NetPacket packet) { if (packet.UserData != null) { if (packet.IsFragmented) { _deliveredFragments.TryGetValue(packet.FragmentId, out var value); value++; if (value == packet.FragmentsTotal) { NetManager.MessageDelivered(this, packet.UserData); _deliveredFragments.Remove(packet.FragmentId); } else { _deliveredFragments[packet.FragmentId] = value; } } else { NetManager.MessageDelivered(this, packet.UserData); } packet.UserData = null; } NetManager.PoolRecycle(packet); } } 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 packetsSent = PacketsSent; long packetLoss = PacketLoss; if (packetsSent != 0L) { return packetLoss * 100 / packetsSent; } return 0L; } } public void Reset() { Interlocked.Exchange(ref _packetsSent, 0L); Interlocked.Exchange(ref _packetsReceived, 0L); Interlocked.Exchange(ref _bytesSent, 0L); Interlocked.Exchange(ref _bytesReceived, 0L); Interlocked.Exchange(ref _packetLoss, 0L); } 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 $"BytesReceived: {BytesReceived}\nPacketsReceived: {PacketsReceived}\nBytesSent: {BytesSent}\nPacketsSent: {PacketsSent}\nPacketLoss: {PacketLoss}\nPacketLossPercent: {PacketLossPercent}\n"; } } [Flags] public enum LocalAddrType { IPv4 = 1, IPv6 = 2, All = 3 } public static class NetUtils { private static readonly NetworkSorter NetworkSorter = new NetworkSorter(); private static readonly List IpList = new List(); public static IPEndPoint MakeEndPoint(string hostStr, int port) { return new IPEndPoint(ResolveAddress(hostStr), port); } public static IPAddress ResolveAddress(string hostStr) { if (hostStr == "localhost") { return IPAddress.Loopback; } if (!IPAddress.TryParse(hostStr, out IPAddress address)) { if (NetManager.IPv6Support) { address = ResolveAddress(hostStr, AddressFamily.InterNetworkV6); } if (address == null) { address = ResolveAddress(hostStr, AddressFamily.InterNetwork); } } if (address == null) { throw new ArgumentException("Invalid address: " + hostStr); } return address; } public static IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily) { IPAddress[] addressList = Dns.GetHostEntry(hostStr).AddressList; foreach (IPAddress iPAddress in addressList) { if (iPAddress.AddressFamily == addressFamily) { return iPAddress; } } return null; } public static List GetLocalIpList(LocalAddrType addrType) { List list = new List(); GetLocalIpList(list, addrType); return list; } public static void GetLocalIpList(IList targetList, LocalAddrType addrType) { bool flag = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4; bool flag2 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6; try { NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); Array.Sort(allNetworkInterfaces, NetworkSorter); NetworkInterface[] array = allNetworkInterfaces; foreach (NetworkInterface networkInterface in array) { if (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Loopback || networkInterface.OperationalStatus != OperationalStatus.Up) { continue; } IPInterfaceProperties iPProperties = networkInterface.GetIPProperties(); if (iPProperties.GatewayAddresses.Count == 0) { continue; } foreach (UnicastIPAddressInformation unicastAddress in iPProperties.UnicastAddresses) { IPAddress address = unicastAddress.Address; if ((flag && address.AddressFamily == AddressFamily.InterNetwork) || (flag2 && address.AddressFamily == AddressFamily.InterNetworkV6)) { targetList.Add(address.ToString()); } } } if (targetList.Count == 0) { IPAddress[] addressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList; foreach (IPAddress iPAddress in addressList) { if ((flag && iPAddress.AddressFamily == AddressFamily.InterNetwork) || (flag2 && iPAddress.AddressFamily == AddressFamily.InterNetworkV6)) { targetList.Add(iPAddress.ToString()); } } } } catch { } if (targetList.Count == 0) { if (flag) { targetList.Add("127.0.0.1"); } if (flag2) { targetList.Add("::1"); } } } public static string GetLocalIp(LocalAddrType addrType) { lock (IpList) { IpList.Clear(); GetLocalIpList(IpList, addrType); return (IpList.Count == 0) ? string.Empty : IpList[0]; } } internal static void PrintInterfaceInfos() { try { NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); for (int i = 0; i < allNetworkInterfaces.Length; i++) { foreach (UnicastIPAddressInformation unicastAddress in allNetworkInterfaces[i].GetIPProperties().UnicastAddresses) { if (unicastAddress.Address.AddressFamily != AddressFamily.InterNetwork) { _ = unicastAddress.Address.AddressFamily; _ = 23; } } } } catch (Exception) { } } internal static int RelativeSequenceNumber(int number, int expected) { return (number - expected + 32768 + 16384) % 32768 - 16384; } internal static T[] AllocatePinnedUninitializedArray(int count) where T : unmanaged { return new T[count]; } } internal class NetworkSorter : IComparer { public int Compare(NetworkInterface a, NetworkInterface b) { bool num = a.NetworkInterfaceType == NetworkInterfaceType.Wman || a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2; bool flag = b.NetworkInterfaceType == NetworkInterfaceType.Wman || b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2; bool flag2 = a.NetworkInterfaceType == NetworkInterfaceType.Wireless80211; bool flag3 = b.NetworkInterfaceType == NetworkInterfaceType.Wireless80211; bool flag4 = a.NetworkInterfaceType == NetworkInterfaceType.Ethernet || a.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || a.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT; bool flag5 = b.NetworkInterfaceType == NetworkInterfaceType.Ethernet || b.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || b.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT; bool flag6 = !num && !flag2 && !flag4; bool flag7 = !flag && !flag3 && !flag5; int num2 = (flag4 ? 3 : (flag2 ? 2 : (flag6 ? 1 : 0))); int num3 = (flag5 ? 3 : (flag3 ? 2 : (flag7 ? 1 : 0))); if (num2 <= num3) { return (num2 < num3) ? 1 : 0; } return -1; } } public readonly ref struct PooledPacket { internal readonly NetPacket _packet; internal readonly byte _channelNumber; public readonly int MaxUserDataSize; public readonly int UserDataOffset; 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; } } internal sealed class ReliableChannel : BaseChannel { private struct PendingPacket { private NetPacket _packet; private long _timeStamp; private bool _isSent; public override string ToString() { if (_packet != null) { return _packet.Sequence.ToString(); } return "Empty"; } public void Init(NetPacket packet) { _packet = packet; _isSent = false; } public bool TrySend(long currentTime, NetPeer peer) { if (_packet == null) { return false; } if (_isSent) { double num = peer.ResendDelay * 10000.0; if ((double)(currentTime - _timeStamp) < num) { return true; } } _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; private readonly PendingPacket[] _pendingPackets; private readonly NetPacket[] _receivedPackets; private readonly bool[] _earlyReceived; 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 = 64; _ordered = ordered; _pendingPackets = new PendingPacket[_windowSize]; for (int i = 0; i < _pendingPackets.Length; i++) { _pendingPackets[i] = default(PendingPacket); } if (_ordered) { _deliveryMethod = DeliveryMethod.ReliableOrdered; _receivedPackets = new NetPacket[_windowSize]; } else { _deliveryMethod = DeliveryMethod.ReliableUnordered; _earlyReceived = new bool[_windowSize]; } _localWindowStart = 0; _localSeqence = 0; _remoteSequence = 0; _remoteWindowStart = 0; _outgoingAcks = new NetPacket(PacketProperty.Ack, (_windowSize - 1) / 8 + 2) { ChannelId = id }; } private void ProcessAck(NetPacket packet) { if (packet.Size != _outgoingAcks.Size) { return; } ushort sequence = packet.Sequence; int num = NetUtils.RelativeSequenceNumber(_localWindowStart, sequence); if (sequence >= 32768 || num < 0 || num >= _windowSize) { return; } byte[] rawData = packet.RawData; lock (_pendingPackets) { int num2 = _localWindowStart; while (num2 != _localSeqence && NetUtils.RelativeSequenceNumber(num2, sequence) < _windowSize) { int num3 = num2 % _windowSize; int num4 = 4 + num3 / 8; int num5 = num3 % 8; if ((rawData[num4] & (1 << num5)) == 0) { if (Peer.NetManager.EnableStatistics) { Peer.Statistics.IncrementPacketLoss(); Peer.NetManager.Statistics.IncrementPacketLoss(); } } else { if (num2 == _localWindowStart) { _localWindowStart = (_localWindowStart + 1) % 32768; } _pendingPackets[num3].Clear(Peer); } num2 = (num2 + 1) % 32768; } } } protected override bool SendNextPackets() { if (_mustSendAcks) { _mustSendAcks = false; lock (_outgoingAcks) { Peer.SendUserData(_outgoingAcks); } } long ticks = DateTime.UtcNow.Ticks; bool flag = false; lock (_pendingPackets) { lock (OutgoingQueue) { while (OutgoingQueue.Count > 0 && NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart) < _windowSize) { NetPacket netPacket = OutgoingQueue.Dequeue(); netPacket.Sequence = (ushort)_localSeqence; netPacket.ChannelId = _id; _pendingPackets[_localSeqence % _windowSize].Init(netPacket); _localSeqence = (_localSeqence + 1) % 32768; } } for (int num = _localWindowStart; num != _localSeqence; num = (num + 1) % 32768) { if (_pendingPackets[num % _windowSize].TrySend(ticks, Peer)) { flag = true; } } } if (!flag && !_mustSendAcks) { return OutgoingQueue.Count > 0; } return true; } public override bool ProcessPacket(NetPacket packet) { if (packet.Property == PacketProperty.Ack) { ProcessAck(packet); return false; } int sequence = packet.Sequence; if (sequence >= 32768) { return false; } int num = NetUtils.RelativeSequenceNumber(sequence, _remoteWindowStart); if (NetUtils.RelativeSequenceNumber(sequence, _remoteSequence) > _windowSize) { return false; } if (num < 0) { return false; } if (num >= _windowSize * 2) { return false; } int num3; lock (_outgoingAcks) { int num4; int num5; if (num >= _windowSize) { int num2 = (_remoteWindowStart + num - _windowSize + 1) % 32768; _outgoingAcks.Sequence = (ushort)num2; while (_remoteWindowStart != num2) { num3 = _remoteWindowStart % _windowSize; num4 = 4 + num3 / 8; num5 = num3 % 8; _outgoingAcks.RawData[num4] &= (byte)(~(1 << num5)); _remoteWindowStart = (_remoteWindowStart + 1) % 32768; } } _mustSendAcks = true; num3 = sequence % _windowSize; num4 = 4 + num3 / 8; num5 = num3 % 8; if ((_outgoingAcks.RawData[num4] & (1 << num5)) != 0) { AddToPeerChannelSendQueue(); return false; } _outgoingAcks.RawData[num4] |= (byte)(1 << num5); } AddToPeerChannelSendQueue(); if (sequence == _remoteSequence) { Peer.AddReliablePacket(_deliveryMethod, packet); _remoteSequence = (_remoteSequence + 1) % 32768; if (_ordered) { NetPacket p; while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) { _receivedPackets[_remoteSequence % _windowSize] = null; Peer.AddReliablePacket(_deliveryMethod, p); _remoteSequence = (_remoteSequence + 1) % 32768; } } else { while (_earlyReceived[_remoteSequence % _windowSize]) { _earlyReceived[_remoteSequence % _windowSize] = false; _remoteSequence = (_remoteSequence + 1) % 32768; } } return true; } if (_ordered) { _receivedPackets[num3] = packet; } else { _earlyReceived[num3] = true; Peer.AddReliablePacket(_deliveryMethod, packet); } return true; } } internal sealed class SequencedChannel : BaseChannel { private int _localSequence; private ushort _remoteSequence; private readonly bool _reliable; private NetPacket _lastPacket; private readonly NetPacket _ackPacket; private bool _mustSendAck; private readonly byte _id; private long _lastPacketSendTime; public SequencedChannel(NetPeer peer, bool reliable, byte id) : base(peer) { _id = id; _reliable = reliable; if (_reliable) { _ackPacket = new NetPacket(PacketProperty.Ack, 0) { ChannelId = id }; } } protected override bool SendNextPackets() { if (_reliable && OutgoingQueue.Count == 0) { long ticks = DateTime.UtcNow.Ticks; if ((double)(ticks - _lastPacketSendTime) >= Peer.ResendDelay * 10000.0) { NetPacket lastPacket = _lastPacket; if (lastPacket != null) { _lastPacketSendTime = ticks; Peer.SendUserData(lastPacket); } } } else { lock (OutgoingQueue) { while (OutgoingQueue.Count > 0) { NetPacket netPacket = OutgoingQueue.Dequeue(); _localSequence = (_localSequence + 1) % 32768; netPacket.Sequence = (ushort)_localSequence; netPacket.ChannelId = _id; Peer.SendUserData(netPacket); if (_reliable && OutgoingQueue.Count == 0) { _lastPacketSendTime = DateTime.UtcNow.Ticks; _lastPacket = netPacket; } else { Peer.NetManager.PoolRecycle(netPacket); } } } } 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 num = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence); bool result = false; if (packet.Sequence < 32768 && num > 0) { if (Peer.NetManager.EnableStatistics) { Peer.Statistics.AddPacketLoss(num - 1); Peer.NetManager.Statistics.AddPacketLoss(num - 1); } _remoteSequence = packet.Sequence; Peer.NetManager.CreateReceiveEvent(packet, (!_reliable) ? DeliveryMethod.Sequenced : DeliveryMethod.ReliableSequenced, (byte)(packet.ChannelId / 4), 4, Peer); result = true; } if (_reliable) { _mustSendAck = true; AddToPeerChannelSendQueue(); } return result; } } } namespace LiteNetLib.Utils { public static class CRC32C { public const int ChecksumSize = 4; private const uint Poly = 2197175160u; private static readonly uint[] Table; static CRC32C() { Table = NetUtils.AllocatePinnedUninitializedArray(4096); for (uint num = 0u; num < 256; num++) { uint num2 = num; for (int i = 0; i < 16; i++) { for (int j = 0; j < 8; j++) { num2 = (((num2 & 1) == 1) ? (0x82F63B78u ^ (num2 >> 1)) : (num2 >> 1)); } Table[i * 256 + num] = num2; } } } public static uint Compute(byte[] input, int offset, int length) { uint num = uint.MaxValue; while (length >= 16) { uint num2 = Table[768 + input[offset + 12]] ^ Table[512 + input[offset + 13]] ^ Table[256 + input[offset + 14]] ^ Table[input[offset + 15]]; uint num3 = Table[1792 + input[offset + 8]] ^ Table[1536 + input[offset + 9]] ^ Table[1280 + input[offset + 10]] ^ Table[1024 + input[offset + 11]]; uint num4 = Table[2816 + input[offset + 4]] ^ Table[2560 + input[offset + 5]] ^ Table[2304 + input[offset + 6]] ^ Table[2048 + input[offset + 7]]; num = Table[3840 + ((byte)num ^ input[offset])] ^ Table[3584 + ((byte)(num >> 8) ^ input[offset + 1])] ^ Table[3328 + ((byte)(num >> 16) ^ input[offset + 2])] ^ Table[3072 + ((num >> 24) ^ input[offset + 3])] ^ num4 ^ num3 ^ num2; offset += 16; length -= 16; } while (--length >= 0) { num = Table[(byte)(num ^ input[offset++])] ^ (num >> 8); } return num ^ 0xFFFFFFFFu; } } public static class FastBitConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void GetBytes(byte[] bytes, int startIndex, T value) where T : unmanaged { int num = sizeof(T); if (bytes.Length < startIndex + num) { ThrowIndexOutOfRangeException(); } fixed (byte* ptr = &bytes[startIndex]) { *(T*)ptr = value; } } private static void ThrowIndexOutOfRangeException() { throw new IndexOutOfRangeException(); } } public interface INetSerializable { void Serialize(NetDataWriter writer); void Deserialize(NetDataReader reader); } public class NetDataReader { protected byte[] _data; protected int _position; protected int _dataSize; private int _offset; public byte[] RawData { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _data; } } public int RawDataSize { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _dataSize; } } public int UserDataOffset { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _offset; } } public int UserDataSize { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _dataSize - _offset; } } public bool IsNull { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _data == null; } } public int Position { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _position; } } public bool EndOfData { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _position == _dataSize; } } public int AvailableBytes { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _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; _offset = 0; _dataSize = dataWriter.Length; } public void SetSource(byte[] source) { _data = source; _position = 0; _offset = 0; _dataSize = source.Length; } public void SetSource(byte[] source, int offset, int maxSize) { _data = source; _position = offset; _offset = offset; _dataSize = maxSize; } public NetDataReader() { } public NetDataReader(NetDataWriter writer) { SetSource(writer); } public NetDataReader(byte[] source) { SetSource(source); } public NetDataReader(byte[] source, int offset, int maxSize) { SetSource(source, offset, maxSize); } public void Get(out T result) where T : struct, INetSerializable { result = default(T); result.Deserialize(this); } public void Get(out T result, Func 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 void Get(out Guid result) { result = GetGuid(); } public IPEndPoint GetNetEndPoint() { string @string = GetString(1000); int @int = GetInt(); return NetUtils.MakeEndPoint(@string, @int); } public byte GetByte() { byte result = _data[_position]; _position++; return result; } public sbyte GetSByte() { return (sbyte)GetByte(); } public T[] GetArray(ushort size) { ushort num = BitConverter.ToUInt16(_data, _position); _position += 2; T[] array = new T[num]; num *= size; Buffer.BlockCopy(_data, _position, array, 0, num); _position += num; return array; } public T[] GetArray() where T : INetSerializable, new() { ushort num = BitConverter.ToUInt16(_data, _position); _position += 2; T[] array = new T[num]; for (int i = 0; i < num; i++) { T val = new T(); val.Deserialize(this); array[i] = val; } return array; } public T[] GetArray(Func constructor) where T : class, INetSerializable { ushort num = BitConverter.ToUInt16(_data, _position); _position += 2; T[] array = new T[num]; for (int i = 0; i < num; i++) { Get(out array[i], constructor); } return array; } public bool[] GetBoolArray() { return GetArray(1); } public ushort[] GetUShortArray() { return GetArray(2); } public short[] GetShortArray() { return GetArray(2); } public int[] GetIntArray() { return GetArray(4); } public uint[] GetUIntArray() { return GetArray(4); } public float[] GetFloatArray() { return GetArray(4); } public double[] GetDoubleArray() { return GetArray(8); } public long[] GetLongArray() { return GetArray(8); } public ulong[] GetULongArray() { return GetArray(8); } public string[] GetStringArray() { ushort uShort = GetUShort(); string[] array = new string[uShort]; for (int i = 0; i < uShort; i++) { array[i] = GetString(); } return array; } public string[] GetStringArray(int maxStringLength) { ushort uShort = GetUShort(); string[] array = new string[uShort]; for (int i = 0; i < uShort; i++) { array[i] = GetString(maxStringLength); } return array; } 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; } public string GetString(int maxLength) { ushort uShort = GetUShort(); if (uShort == 0) { return string.Empty; } int num = uShort - 1; string result = ((maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position, num) > maxLength) ? string.Empty : NetDataWriter.uTF8Encoding.Value.GetString(_data, _position, num)); _position += num; return result; } public string GetString() { ushort uShort = GetUShort(); if (uShort == 0) { return string.Empty; } int num = uShort - 1; string @string = NetDataWriter.uTF8Encoding.Value.GetString(_data, _position, num); _position += num; return @string; } public string GetLargeString() { int @int = GetInt(); if (@int <= 0) { return string.Empty; } string @string = NetDataWriter.uTF8Encoding.Value.GetString(_data, _position, @int); _position += @int; return @string; } public Guid GetGuid() { Guid result = new Guid(_data.AsSpan(_position, 16)); _position += 16; return result; } public ArraySegment GetBytesSegment(int count) { ArraySegment result = new ArraySegment(_data, _position, count); _position += count; return result; } public ArraySegment GetRemainingBytesSegment() { ArraySegment result = new ArraySegment(_data, _position, AvailableBytes); _position = _data.Length; return result; } public T Get() where T : struct, INetSerializable { T result = default(T); result.Deserialize(this); return result; } public T Get(Func constructor) where T : class, INetSerializable { T val = constructor(); val.Deserialize(this); return val; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan GetRemainingBytesSpan() { return new ReadOnlySpan(_data, _position, _dataSize - _position); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlyMemory GetRemainingBytesMemory() { return new ReadOnlyMemory(_data, _position, _dataSize - _position); } public byte[] GetRemainingBytes() { byte[] array = new byte[AvailableBytes]; Buffer.BlockCopy(_data, _position, array, 0, AvailableBytes); _position = _data.Length; return array; } 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(1); } public byte[] GetBytesWithLength() { return GetArray(1); } 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); } public string PeekString(int maxLength) { ushort num = PeekUShort(); if (num == 0) { return string.Empty; } int count = num - 1; if (maxLength <= 0 || NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position + 2, count) <= maxLength) { return NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, count); } return string.Empty; } public string PeekString() { ushort num = PeekUShort(); if (num == 0) { return string.Empty; } int count = num - 1; return NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, count); } 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 var result2)) { result = '\0'; return false; } result = (char)result2; 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 = 0u; return false; } public bool TryGetLong(out long result) { if (AvailableBytes >= 8) { result = GetLong(); return true; } result = 0L; return false; } public bool TryGetULong(out ulong result) { if (AvailableBytes >= 8) { result = GetULong(); return true; } result = 0uL; return false; } public bool TryGetFloat(out float result) { if (AvailableBytes >= 4) { result = GetFloat(); return true; } result = 0f; return false; } public bool TryGetDouble(out double result) { if (AvailableBytes >= 8) { result = GetDouble(); return true; } result = 0.0; return false; } public bool TryGetString(out string result) { if (AvailableBytes >= 2) { ushort num = PeekUShort(); if (AvailableBytes >= num + 1) { result = GetString(); return true; } } result = null; return false; } public bool TryGetStringArray(out string[] result) { if (!TryGetUShort(out var result2)) { result = null; return false; } result = new string[result2]; for (int i = 0; i < result2; i++) { if (!TryGetString(out result[i])) { result = null; return false; } } return true; } public bool TryGetBytesWithLength(out byte[] result) { if (AvailableBytes >= 2) { ushort num = PeekUShort(); if (num >= 0 && AvailableBytes >= 2 + num) { result = GetBytesWithLength(); return true; } } result = null; return false; } public void Clear() { _position = 0; _dataSize = 0; _data = null; } } public class NetDataWriter { protected byte[] _data; protected int _position; private const int InitialSize = 64; private readonly bool _autoResize; public static readonly ThreadLocal uTF8Encoding = new ThreadLocal(() => new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _data.Length; } } public byte[] Data { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _data; } } public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _position; } } public ReadOnlySpan AsReadOnlySpan() { return new ReadOnlySpan(_data, 0, _position); } public NetDataWriter() : this(autoResize: true, 64) { } public NetDataWriter(bool autoResize) : this(autoResize, 64) { } public NetDataWriter(bool autoResize, int initialSize) { _data = new byte[initialSize]; _autoResize = autoResize; } public static NetDataWriter FromBytes(byte[] bytes, bool copy) { if (copy) { NetDataWriter netDataWriter = new NetDataWriter(autoResize: true, bytes.Length); netDataWriter.Put(bytes); return netDataWriter; } return new NetDataWriter(autoResize: true, 0) { _data = bytes, _position = bytes.Length }; } public static NetDataWriter FromBytes(byte[] bytes, int offset, int length) { NetDataWriter netDataWriter = new NetDataWriter(autoResize: true, bytes.Length); netDataWriter.Put(bytes, offset, length); return netDataWriter; } public static NetDataWriter FromBytes(Span bytes) { NetDataWriter netDataWriter = new NetDataWriter(autoResize: true, bytes.Length); netDataWriter.Put((ReadOnlySpan)bytes); return netDataWriter; } public static NetDataWriter FromString(string value) { NetDataWriter netDataWriter = new NetDataWriter(); netDataWriter.Put(value); return netDataWriter; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ResizeIfNeed(int newSize) { if (_data.Length < newSize) { Array.Resize(ref _data, Math.Max(newSize, _data.Length * 2)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EnsureFit(int additionalSize) { if (_data.Length < _position + additionalSize) { Array.Resize(ref _data, Math.Max(_position + additionalSize, _data.Length * 2)); } } public void Reset(int size) { ResizeIfNeed(size); _position = 0; } public void Reset() { _position = 0; } public byte[] CopyData() { byte[] array = new byte[_position]; Buffer.BlockCopy(_data, 0, array, 0, _position); return array; } public int SetPosition(int position) { int position2 = _position; _position = position; return position2; } public void Put(float value) { if (_autoResize) { ResizeIfNeed(_position + 4); } FastBitConverter.GetBytes(_data, _position, value); _position += 4; } public void Put(double value) { if (_autoResize) { ResizeIfNeed(_position + 8); } FastBitConverter.GetBytes(_data, _position, value); _position += 8; } public void Put(long value) { if (_autoResize) { ResizeIfNeed(_position + 8); } FastBitConverter.GetBytes(_data, _position, value); _position += 8; } public void Put(ulong value) { if (_autoResize) { ResizeIfNeed(_position + 8); } FastBitConverter.GetBytes(_data, _position, value); _position += 8; } public void Put(int value) { if (_autoResize) { ResizeIfNeed(_position + 4); } FastBitConverter.GetBytes(_data, _position, value); _position += 4; } public void Put(uint value) { if (_autoResize) { ResizeIfNeed(_position + 4); } FastBitConverter.GetBytes(_data, _position, value); _position += 4; } public void Put(char value) { Put((ushort)value); } public void Put(ushort value) { if (_autoResize) { ResizeIfNeed(_position + 2); } FastBitConverter.GetBytes(_data, _position, value); _position += 2; } public void Put(short value) { if (_autoResize) { ResizeIfNeed(_position + 2); } FastBitConverter.GetBytes(_data, _position, value); _position += 2; } public void Put(sbyte value) { if (_autoResize) { ResizeIfNeed(_position + 1); } _data[_position] = (byte)value; _position++; } public void Put(byte value) { if (_autoResize) { ResizeIfNeed(_position + 1); } _data[_position] = value; _position++; } public void Put(Guid value) { if (_autoResize) { ResizeIfNeed(_position + 16); } value.TryWriteBytes(_data.AsSpan(_position)); _position += 16; } public void Put(byte[] data, int offset, int length) { if (_autoResize) { ResizeIfNeed(_position + length); } Buffer.BlockCopy(data, offset, _data, _position, length); _position += length; } public void Put(byte[] data) { if (_autoResize) { ResizeIfNeed(_position + data.Length); } Buffer.BlockCopy(data, 0, _data, _position, data.Length); _position += data.Length; } public void Put(ReadOnlySpan data) { if (_autoResize) { ResizeIfNeed(_position + data.Length); } data.CopyTo(_data.AsSpan(_position)); _position += data.Length; } public void PutSBytesWithLength(sbyte[] data, int offset, ushort length) { if (_autoResize) { ResizeIfNeed(_position + 2 + length); } FastBitConverter.GetBytes(_data, _position, length); Buffer.BlockCopy(data, offset, _data, _position + 2, length); _position += 2 + length; } public void PutSBytesWithLength(sbyte[] data) { PutArray(data, 1); } public void PutBytesWithLength(byte[] data, int offset, ushort length) { if (_autoResize) { ResizeIfNeed(_position + 2 + length); } FastBitConverter.GetBytes(_data, _position, length); Buffer.BlockCopy(data, offset, _data, _position + 2, length); _position += 2 + length; } public void PutBytesWithLength(byte[] data) { PutArray(data, 1); } public void Put(bool value) { Put(value ? ((byte)1) : ((byte)0)); } public void PutArray(Array arr, int sz) { ushort num = (ushort)((arr != null) ? ((ushort)arr.Length) : 0); sz *= num; if (_autoResize) { ResizeIfNeed(_position + sz + 2); } FastBitConverter.GetBytes(_data, _position, num); if (arr != null) { Buffer.BlockCopy(arr, 0, _data, _position + 2, sz); } _position += sz + 2; } public void PutArray(float[] value) { PutArray(value, 4); } public void PutArray(double[] value) { PutArray(value, 8); } public void PutArray(long[] value) { PutArray(value, 8); } public void PutArray(ulong[] value) { PutArray(value, 8); } public void PutArray(int[] value) { PutArray(value, 4); } public void PutArray(uint[] value) { PutArray(value, 4); } public void PutArray(ushort[] value) { PutArray(value, 2); } public void PutArray(short[] value) { PutArray(value, 2); } public void PutArray(bool[] value) { PutArray(value, 1); } public void PutArray(string[] value) { ushort num = (ushort)((value != null) ? ((ushort)value.Length) : 0); Put(num); for (int i = 0; i < num; i++) { Put(value[i]); } } public void PutArray(string[] value, int strMaxLength) { ushort num = (ushort)((value != null) ? ((ushort)value.Length) : 0); Put(num); for (int i = 0; i < num; i++) { Put(value[i], strMaxLength); } } public void PutArray(T[] value) where T : INetSerializable, new() { ushort num = (ushort)((value != null) ? ((uint)value.Length) : 0u); Put(num); for (int i = 0; i < num; i++) { value[i].Serialize(this); } } public void Put(IPEndPoint endPoint) { Put(endPoint.Address.ToString()); Put(endPoint.Port); } public void PutLargeString(string value) { if (string.IsNullOrEmpty(value)) { Put(0); return; } int byteCount = uTF8Encoding.Value.GetByteCount(value); if (byteCount == 0) { Put(0); return; } Put(byteCount); if (_autoResize) { ResizeIfNeed(_position + byteCount); } uTF8Encoding.Value.GetBytes(value, 0, byteCount, _data, _position); _position += byteCount; } public void Put(string value) { Put(value, 0); } public void Put(string value, int maxLength) { if (string.IsNullOrEmpty(value)) { Put((ushort)0); return; } int charCount = ((maxLength > 0 && value.Length > maxLength) ? maxLength : value.Length); int maxByteCount = uTF8Encoding.Value.GetMaxByteCount(charCount); if (_autoResize) { ResizeIfNeed(_position + maxByteCount + 2); } int bytes = uTF8Encoding.Value.GetBytes(value, 0, charCount, _data, _position + 2); if (bytes == 0) { Put((ushort)0); return; } Put(checked((ushort)(bytes + 1))); _position += bytes; } public void Put(T obj) where T : INetSerializable { obj.Serialize(this); } } public class NetPacketProcessor { private static class HashCache { public static readonly ulong Id; static HashCache() { ulong num = 14695981039346656037uL; string text = typeof(T).ToString(); for (int i = 0; i < text.Length; i++) { num ^= text[i]; num *= 1099511628211L; } Id = num; } } protected delegate void SubscribeDelegate(NetDataReader reader, object userData); private readonly NetSerializer _netSerializer; private readonly Dictionary _callbacks = new Dictionary(); public NetPacketProcessor() { _netSerializer = new NetSerializer(); } public NetPacketProcessor(int maxStringLength) { _netSerializer = new NetSerializer(maxStringLength); } protected virtual ulong GetHash() { return HashCache.Id; } protected virtual SubscribeDelegate GetCallbackFromData(NetDataReader reader) { ulong uLong = reader.GetULong(); if (!_callbacks.TryGetValue(uLong, out var value)) { throw new ParseException("Undefined packet in NetDataReader"); } return value; } protected virtual void WriteHash(NetDataWriter writer) { writer.Put(GetHash()); } public void RegisterNestedType() where T : struct, INetSerializable { _netSerializer.RegisterNestedType(); } public void RegisterNestedType(Action writeDelegate, Func readDelegate) { _netSerializer.RegisterNestedType(writeDelegate, readDelegate); } public void RegisterNestedType(Func constructor) where T : class, INetSerializable { _netSerializer.RegisterNestedType(constructor); } public void ReadAllPackets(NetDataReader reader) { while (reader.AvailableBytes > 0) { ReadPacket(reader); } } public void ReadAllPackets(NetDataReader reader, object userData) { while (reader.AvailableBytes > 0) { ReadPacket(reader, userData); } } public void ReadPacket(NetDataReader reader) { ReadPacket(reader, null); } public void Write(NetDataWriter writer, T packet) where T : class, new() { WriteHash(writer); _netSerializer.Serialize(writer, packet); } public void WriteNetSerializable(NetDataWriter writer, ref T packet) where T : INetSerializable { WriteHash(writer); packet.Serialize(writer); } public void ReadPacket(NetDataReader reader, object userData) { GetCallbackFromData(reader)(reader, userData); } public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() { _netSerializer.Register(); _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { T val = packetConstructor(); _netSerializer.Deserialize(reader, val); onReceive(val); }; } public void Subscribe(Action onReceive, Func packetConstructor) where T : class, new() { _netSerializer.Register(); _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { T val = packetConstructor(); _netSerializer.Deserialize(reader, val); onReceive(val, (TUserData)userData); }; } public void SubscribeReusable(Action onReceive) where T : class, new() { _netSerializer.Register(); T reference = new T(); _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { _netSerializer.Deserialize(reader, reference); onReceive(reference); }; } public void SubscribeReusable(Action onReceive) where T : class, new() { _netSerializer.Register(); T reference = new T(); _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { _netSerializer.Deserialize(reader, reference); onReceive(reference, (TUserData)userData); }; } public void SubscribeNetSerializable(Action onReceive, Func packetConstructor) where T : INetSerializable { _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { T arg = packetConstructor(); arg.Deserialize(reader); onReceive(arg, (TUserData)userData); }; } public void SubscribeNetSerializable(Action onReceive, Func packetConstructor) where T : INetSerializable { _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { T obj = packetConstructor(); obj.Deserialize(reader); onReceive(obj); }; } public void SubscribeNetSerializable(Action onReceive) where T : INetSerializable, new() { T reference = new T(); _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { reference.Deserialize(reader); onReceive(reference, (TUserData)userData); }; } public void SubscribeNetSerializable(Action onReceive) where T : INetSerializable, new() { T reference = new T(); _callbacks[GetHash()] = delegate(NetDataReader reader, object userData) { reference.Deserialize(reader); onReceive(reference); }; } public bool RemoveSubscription() { return _callbacks.Remove(GetHash()); } } public class InvalidTypeException : ArgumentException { public InvalidTypeException(string message) : base(message) { } } public class ParseException : Exception { public ParseException(string message) : base(message) { } } public class NetSerializer { private enum CallType { Basic, Array, List } private abstract class FastCall { public CallType Type; public virtual void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { Type = type; } public abstract void Read(T inf, NetDataReader r); public abstract void Write(T inf, NetDataWriter w); public abstract void ReadArray(T inf, NetDataReader r); public abstract void WriteArray(T inf, NetDataWriter w); public abstract void ReadList(T inf, NetDataReader r); public abstract void WriteList(T inf, NetDataWriter w); } private abstract class FastCallSpecific : FastCall { protected Func Getter; protected Action Setter; protected Func GetterArr; protected Action SetterArr; protected Func> GetterList; protected Action> SetterList; public override void ReadArray(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty)?.ToString() + "[]"); } public override void WriteArray(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty)?.ToString() + "[]"); } public override void ReadList(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty)?.ToString() + ">"); } public override void WriteList(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty)?.ToString() + ">"); } protected TProperty[] ReadArrayHelper(TClass inf, NetDataReader r) { ushort uShort = r.GetUShort(); TProperty[] array = GetterArr(inf); array = ((array == null || array.Length != uShort) ? new TProperty[uShort] : array); SetterArr(inf, array); return array; } protected TProperty[] WriteArrayHelper(TClass inf, NetDataWriter w) { TProperty[] array = GetterArr(inf); w.Put((ushort)array.Length); return array; } protected List ReadListHelper(TClass inf, NetDataReader r, out int len) { len = r.GetUShort(); List list = GetterList(inf); if (list == null) { list = new List(len); SetterList(inf, list); } return list; } protected List WriteListHelper(TClass inf, NetDataWriter w, out int len) { List list = GetterList(inf); if (list == null) { len = 0; w.Put(0); return null; } len = list.Count; w.Put((ushort)len); return list; } public override void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { base.Init(getMethod, setMethod, type); switch (type) { case CallType.Array: GetterArr = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); SetterArr = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); break; case CallType.List: GetterList = (Func>)Delegate.CreateDelegate(typeof(Func>), getMethod); SetterList = (Action>)Delegate.CreateDelegate(typeof(Action>), setMethod); break; default: Getter = (Func)Delegate.CreateDelegate(typeof(Func), getMethod); Setter = (Action)Delegate.CreateDelegate(typeof(Action), setMethod); break; } } } private abstract class FastCallSpecificAuto : FastCallSpecific { protected abstract void ElementRead(NetDataReader r, out TProperty prop); protected abstract void ElementWrite(NetDataWriter w, ref TProperty prop); public override void Read(TClass inf, NetDataReader r) { ElementRead(r, out var prop); Setter(inf, prop); } public override void Write(TClass inf, NetDataWriter w) { TProperty prop = Getter(inf); ElementWrite(w, ref prop); } public override void ReadArray(TClass inf, NetDataReader r) { TProperty[] array = ReadArrayHelper(inf, r); for (int i = 0; i < array.Length; i++) { ElementRead(r, out array[i]); } } public override void WriteArray(TClass inf, NetDataWriter w) { TProperty[] array = WriteArrayHelper(inf, w); for (int i = 0; i < array.Length; i++) { ElementWrite(w, ref array[i]); } } } private sealed class FastCallStatic : FastCallSpecific { private readonly Action _writer; private readonly Func _reader; public FastCallStatic(Action write, Func read) { _writer = write; _reader = read; } public override void Read(TClass inf, NetDataReader r) { Setter(inf, _reader(r)); } public override void Write(TClass inf, NetDataWriter w) { _writer(w, Getter(inf)); } public override void ReadList(TClass inf, NetDataReader r) { int len; List list = ReadListHelper(inf, r, out len); int count = list.Count; for (int i = 0; i < len; i++) { if (i < count) { list[i] = _reader(r); } else { list.Add(_reader(r)); } } if (len < count) { list.RemoveRange(len, count - len); } } public override void WriteList(TClass inf, NetDataWriter w) { int len; List list = WriteListHelper(inf, w, out len); for (int i = 0; i < len; i++) { _writer(w, list[i]); } } public override void ReadArray(TClass inf, NetDataReader r) { TProperty[] array = ReadArrayHelper(inf, r); int num = array.Length; for (int i = 0; i < num; i++) { array[i] = _reader(r); } } public override void WriteArray(TClass inf, NetDataWriter w) { TProperty[] array = WriteArrayHelper(inf, w); int num = array.Length; for (int i = 0; i < num; i++) { _writer(w, array[i]); } } } private sealed class FastCallStruct : FastCallSpecific where TProperty : struct, INetSerializable { private TProperty _p; public override void Read(TClass inf, NetDataReader r) { _p.Deserialize(r); Setter(inf, _p); } public override void Write(TClass inf, NetDataWriter w) { _p = Getter(inf); _p.Serialize(w); } public override void ReadList(TClass inf, NetDataReader r) { int len; List list = ReadListHelper(inf, r, out len); int count = list.Count; for (int i = 0; i < len; i++) { TProperty val = default(TProperty); val.Deserialize(r); if (i < count) { list[i] = val; } else { list.Add(val); } } if (len < count) { list.RemoveRange(len, count - len); } } public override void WriteList(TClass inf, NetDataWriter w) { int len; List list = WriteListHelper(inf, w, out len); for (int i = 0; i < len; i++) { list[i].Serialize(w); } } public override void ReadArray(TClass inf, NetDataReader r) { TProperty[] array = ReadArrayHelper(inf, r); int num = array.Length; for (int i = 0; i < num; i++) { array[i].Deserialize(r); } } public override void WriteArray(TClass inf, NetDataWriter w) { TProperty[] array = WriteArrayHelper(inf, w); int num = array.Length; for (int i = 0; i < num; i++) { array[i].Serialize(w); } } } private sealed class FastCallClass : FastCallSpecific where TProperty : class, INetSerializable { private readonly Func _constructor; public FastCallClass(Func constructor) { _constructor = constructor; } public override void Read(TClass inf, NetDataReader r) { TProperty val = _constructor(); val.Deserialize(r); Setter(inf, val); } public override void Write(TClass inf, NetDataWriter w) { Getter(inf)?.Serialize(w); } public override void ReadList(TClass inf, NetDataReader r) { int len; List list = ReadListHelper(inf, r, out len); int count = list.Count; for (int i = 0; i < len; i++) { if (i < count) { list[i].Deserialize(r); continue; } TProperty val = _constructor(); val.Deserialize(r); list.Add(val); } if (len < count) { list.RemoveRange(len, count - len); } } public override void WriteList(TClass inf, NetDataWriter w) { int len; List list = WriteListHelper(inf, w, out len); for (int i = 0; i < len; i++) { list[i].Serialize(w); } } public override void ReadArray(TClass inf, NetDataReader r) { TProperty[] array = ReadArrayHelper(inf, r); int num = array.Length; for (int i = 0; i < num; i++) { array[i] = _constructor(); array[i].Deserialize(r); } } public override void WriteArray(TClass inf, NetDataWriter w) { TProperty[] array = WriteArrayHelper(inf, w); int num = array.Length; for (int i = 0; i < num; i++) { array[i].Serialize(w); } } } private class IntSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetInt()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetIntArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class UIntSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUInt()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUIntArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class ShortSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetShort()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetShortArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class UShortSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUShort()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUShortArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class LongSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetLong()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetLongArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class ULongSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetULong()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetULongArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class ByteSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetByte()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBytesWithLength()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutBytesWithLength(GetterArr(inf)); } } private class SByteSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetSByte()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetSBytesWithLength()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutSBytesWithLength(GetterArr(inf)); } } private class FloatSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetFloat()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetFloatArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class DoubleSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetDouble()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetDoubleArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class BoolSerializer : FastCallSpecific { public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetBool()); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBoolArray()); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } } private class CharSerializer : FastCallSpecificAuto { protected override void ElementWrite(NetDataWriter w, ref char prop) { w.Put(prop); } protected override void ElementRead(NetDataReader r, out char prop) { prop = r.GetChar(); } } private class IPEndPointSerializer : FastCallSpecificAuto { protected override void ElementWrite(NetDataWriter w, ref IPEndPoint prop) { w.Put(prop); } protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); } } private class GuidSerializer : FastCallSpecificAuto { protected override void ElementWrite(NetDataWriter w, ref Guid guid) { w.Put(guid); } protected override void ElementRead(NetDataReader r, out Guid guid) { guid = r.GetGuid(); } } private class StringSerializer : FastCallSpecific { private readonly int _maxLength; public StringSerializer(int maxLength) { _maxLength = ((maxLength > 0) ? maxLength : 32767); } public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetString(_maxLength)); } public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf), _maxLength); } public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetStringArray(_maxLength)); } public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf), _maxLength); } } private class EnumByteSerializer : FastCall { protected readonly PropertyInfo Property; protected readonly Type PropertyType; public EnumByteSerializer(PropertyInfo property, Type propertyType) { Property = property; PropertyType = propertyType; } public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetByte()), null); } public override void Write(T inf, NetDataWriter w) { w.Put((byte)Property.GetValue(inf, null)); } public override void ReadArray(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: Enum[]"); } public override void WriteArray(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: Enum[]"); } public override void ReadList(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List"); } public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List"); } } private class EnumIntSerializer : EnumByteSerializer { public EnumIntSerializer(PropertyInfo property, Type propertyType) : base(property, propertyType) { } public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetInt()), null); } public override void Write(T inf, NetDataWriter w) { w.Put((int)Property.GetValue(inf, null)); } } private sealed class ClassInfo { public static ClassInfo Instance; private readonly FastCall[] _serializers; private readonly int _membersCount; public ClassInfo(List> serializers) { _membersCount = serializers.Count; _serializers = serializers.ToArray(); } public void Write(T obj, NetDataWriter writer) { for (int i = 0; i < _membersCount; i++) { FastCall fastCall = _serializers[i]; if (fastCall.Type == CallType.Basic) { fastCall.Write(obj, writer); } else if (fastCall.Type == CallType.Array) { fastCall.WriteArray(obj, writer); } else { fastCall.WriteList(obj, writer); } } } public void Read(T obj, NetDataReader reader) { for (int i = 0; i < _membersCount; i++) { FastCall fastCall = _serializers[i]; if (fastCall.Type == CallType.Basic) { fastCall.Read(obj, reader); } else if (fastCall.Type == CallType.Array) { fastCall.ReadArray(obj, reader); } else { fastCall.ReadList(obj, reader); } } } } private abstract class CustomType { public abstract FastCall Get(); } private sealed class CustomTypeStruct : CustomType where TProperty : struct, INetSerializable { public override FastCall Get() { return new FastCallStruct(); } } private sealed class CustomTypeClass : CustomType where TProperty : class, INetSerializable { private readonly Func _constructor; public CustomTypeClass(Func constructor) { _constructor = constructor; } public override FastCall Get() { return new FastCallClass(_constructor); } } private sealed class CustomTypeStatic : CustomType { private readonly Action _writer; private readonly Func _reader; public CustomTypeStatic(Action writer, Func reader) { _writer = writer; _reader = reader; } public override FastCall Get() { return new FastCallStatic(_writer, _reader); } } private NetDataWriter _writer; private readonly int _maxStringLength; private readonly Dictionary _registeredTypes = new Dictionary(); public void RegisterNestedType() where T : struct, INetSerializable { _registeredTypes.Add(typeof(T), new CustomTypeStruct()); } public void RegisterNestedType(Func constructor) where T : class, INetSerializable { _registeredTypes.Add(typeof(T), new CustomTypeClass(constructor)); } public void RegisterNestedType(Action writer, Func reader) { _registeredTypes.Add(typeof(T), new CustomTypeStatic(writer, reader)); } public NetSerializer() : this(0) { } public NetSerializer(int maxStringLength) { _maxStringLength = maxStringLength; } private ClassInfo RegisterInternal() { if (ClassInfo.Instance != null) { return ClassInfo.Instance; } PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty); List> list = new List>(); foreach (PropertyInfo propertyInfo in properties) { Type propertyType = propertyInfo.PropertyType; Type type = (propertyType.IsArray ? propertyType.GetElementType() : propertyType); CallType type2 = (propertyType.IsArray ? CallType.Array : CallType.Basic); if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)) { type = propertyType.GetGenericArguments()[0]; type2 = CallType.List; } if (Attribute.IsDefined(propertyInfo, typeof(IgnoreDataMemberAttribute))) { continue; } MethodInfo getMethod = propertyInfo.GetGetMethod(); MethodInfo setMethod = propertyInfo.GetSetMethod(); if (getMethod == null || setMethod == null) { continue; } FastCall fastCall = null; if (propertyType.IsEnum) { Type underlyingType = Enum.GetUnderlyingType(propertyType); if (underlyingType == typeof(byte)) { fastCall = new EnumByteSerializer(propertyInfo, propertyType); } else { if (!(underlyingType == typeof(int))) { throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name); } fastCall = new EnumIntSerializer(propertyInfo, propertyType); } } else if (type == typeof(string)) { fastCall = new StringSerializer(_maxStringLength); } else if (type == typeof(bool)) { fastCall = new BoolSerializer(); } else if (type == typeof(byte)) { fastCall = new ByteSerializer(); } else if (type == typeof(sbyte)) { fastCall = new SByteSerializer(); } else if (type == typeof(short)) { fastCall = new ShortSerializer(); } else if (type == typeof(ushort)) { fastCall = new UShortSerializer(); } else if (type == typeof(int)) { fastCall = new IntSerializer(); } else if (type == typeof(uint)) { fastCall = new UIntSerializer(); } else if (type == typeof(long)) { fastCall = new LongSerializer(); } else if (type == typeof(ulong)) { fastCall = new ULongSerializer(); } else if (type == typeof(float)) { fastCall = new FloatSerializer(); } else if (type == typeof(double)) { fastCall = new DoubleSerializer(); } else if (type == typeof(char)) { fastCall = new CharSerializer(); } else if (type == typeof(IPEndPoint)) { fastCall = new IPEndPointSerializer(); } else if (type == typeof(Guid)) { fastCall = new GuidSerializer(); } else { _registeredTypes.TryGetValue(type, out var value); if (value != null) { fastCall = value.Get(); } } if (fastCall != null) { fastCall.Init(getMethod, setMethod, type2); list.Add(fastCall); continue; } throw new InvalidTypeException("Unknown property type: " + propertyType.FullName); } ClassInfo.Instance = new ClassInfo(list); return ClassInfo.Instance; } public void Register() { RegisterInternal(); } public T Deserialize(NetDataReader reader) where T : class, new() { ClassInfo classInfo = RegisterInternal(); T val = new T(); try { classInfo.Read(val, reader); return val; } catch { return null; } } public bool Deserialize(NetDataReader reader, T target) where T : class, new() { ClassInfo classInfo = RegisterInternal(); try { classInfo.Read(target, reader); } catch { return false; } return true; } public void Serialize(NetDataWriter writer, T obj) where T : class, new() { RegisterInternal().Write(obj, writer); } public byte[] Serialize(T obj) where T : class, new() { if (_writer == null) { _writer = new NetDataWriter(); } _writer.Reset(); Serialize(_writer, obj); return _writer.CopyData(); } } public class NtpPacket { private static readonly DateTime Epoch = new DateTime(1900, 1, 1); public byte[] Bytes { get; } public NtpLeapIndicator LeapIndicator => (NtpLeapIndicator)((Bytes[0] & 0xC0) >> 6); public int VersionNumber { get { return (Bytes[0] & 0x38) >> 3; } private set { Bytes[0] = (byte)((Bytes[0] & 0xFFFFFFC7u) | (uint)(value << 3)); } } public NtpMode Mode { get { return (NtpMode)(Bytes[0] & 7); } private set { Bytes[0] = (byte)((Bytes[0] & 0xFFFFFFF8u) | (uint)value); } } public int Stratum => Bytes[1]; public int Poll => Bytes[2]; public int Precision => (sbyte)Bytes[3]; public TimeSpan RootDelay => GetTimeSpan32(4); public TimeSpan RootDispersion => GetTimeSpan32(8); public uint ReferenceId => GetUInt32BE(12); public DateTime? ReferenceTimestamp => GetDateTime64(16); public DateTime? OriginTimestamp => GetDateTime64(24); public DateTime? ReceiveTimestamp => GetDateTime64(32); public DateTime? TransmitTimestamp { get { return GetDateTime64(40); } private set { SetDateTime64(40, value); } } public DateTime? DestinationTimestamp { get; private set; } public TimeSpan RoundTripTime { get { CheckTimestamps(); return ReceiveTimestamp.Value - OriginTimestamp.Value + (DestinationTimestamp.Value - TransmitTimestamp.Value); } } public TimeSpan CorrectionOffset { get { CheckTimestamps(); return TimeSpan.FromTicks((ReceiveTimestamp.Value - OriginTimestamp.Value - (DestinationTimestamp.Value - TransmitTimestamp.Value)).Ticks / 2); } } public NtpPacket() : this(new byte[48]) { Mode = NtpMode.Client; VersionNumber = 4; TransmitTimestamp = DateTime.UtcNow; } internal NtpPacket(byte[] bytes) { if (bytes.Length < 48) { throw new ArgumentException("SNTP reply packet must be at least 48 bytes long.", "bytes"); } Bytes = bytes; } public static NtpPacket FromServerResponse(byte[] bytes, DateTime destinationTimestamp) { return new NtpPacket(bytes) { DestinationTimestamp = destinationTimestamp }; } internal void ValidateRequest() { if (Mode != NtpMode.Client) { throw new InvalidOperationException("This is not a request SNTP packet."); } if (VersionNumber == 0) { throw new InvalidOperationException("Protocol version of the request is not specified."); } if (!TransmitTimestamp.HasValue) { throw new InvalidOperationException("TransmitTimestamp must be set in request packet."); } } internal void ValidateReply() { if (Mode != NtpMode.Server) { throw new InvalidOperationException("This is not a reply SNTP packet."); } if (VersionNumber == 0) { throw new InvalidOperationException("Protocol version of the reply is not specified."); } if (Stratum == 0) { throw new InvalidOperationException($"Received Kiss-o'-Death SNTP packet with code 0x{ReferenceId:x}."); } if (LeapIndicator == NtpLeapIndicator.AlarmCondition) { throw new InvalidOperationException("SNTP server has unsynchronized clock."); } CheckTimestamps(); } private void CheckTimestamps() { if (!OriginTimestamp.HasValue) { throw new InvalidOperationException("Origin timestamp is missing."); } if (!ReceiveTimestamp.HasValue) { throw new InvalidOperationException("Receive timestamp is missing."); } if (!TransmitTimestamp.HasValue) { throw new InvalidOperationException("Transmit timestamp is missing."); } if (!DestinationTimestamp.HasValue) { throw new InvalidOperationException("Destination timestamp is missing."); } } private DateTime? GetDateTime64(int offset) { ulong uInt64BE = GetUInt64BE(offset); if (uInt64BE == 0L) { return null; } return new DateTime(Epoch.Ticks + Convert.ToInt64((double)uInt64BE * 0.0023283064365386963)); } private void SetDateTime64(int offset, DateTime? value) { SetUInt64BE(offset, (!value.HasValue) ? 0 : Convert.ToUInt64((double)(value.Value.Ticks - Epoch.Ticks) * 429.4967296)); } private TimeSpan GetTimeSpan32(int offset) { return TimeSpan.FromSeconds((double)GetInt32BE(offset) / 65536.0); } private ulong GetUInt64BE(int offset) { return SwapEndianness(BitConverter.ToUInt64(Bytes, offset)); } private void SetUInt64BE(int offset, ulong value) { FastBitConverter.GetBytes(Bytes, offset, SwapEndianness(value)); } private int GetInt32BE(int offset) { return (int)GetUInt32BE(offset); } private uint GetUInt32BE(int offset) { return SwapEndianness(BitConverter.ToUInt32(Bytes, offset)); } private static uint SwapEndianness(uint x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000u) >> 24); } private static ulong SwapEndianness(ulong x) { return ((ulong)SwapEndianness((uint)x) << 32) | SwapEndianness((uint)(x >> 32)); } } public enum NtpLeapIndicator { NoWarning, LastMinuteHas61Seconds, LastMinuteHas59Seconds, AlarmCondition } public enum NtpMode { Client = 3, Server } internal sealed class NtpRequest { private const int ResendTimer = 1000; private const int KillTimer = 10000; public const int DefaultPort = 123; private readonly IPEndPoint _ntpEndPoint; private float _resendTime = 1000f; private float _killTime; public bool NeedToKill => _killTime >= 10000f; public NtpRequest(IPEndPoint endPoint) { _ntpEndPoint = endPoint; } public bool Send(Socket socket, float time) { _resendTime += time; _killTime += time; if (_resendTime < 1000f) { return false; } NtpPacket ntpPacket = new NtpPacket(); try { return socket.SendTo(ntpPacket.Bytes, 0, ntpPacket.Bytes.Length, SocketFlags.None, _ntpEndPoint) == ntpPacket.Bytes.Length; } catch { return false; } } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] public class PreserveAttribute : Attribute { } } namespace LiteNetLib.Layers { public sealed class Crc32cLayer : PacketLayerBase { public Crc32cLayer() : base(4) { } public override void ProcessInboundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int length) { if (length < 5) { NetDebug.WriteError("[NM] DataReceived size: bad!"); length = 0; return; } int num = length - 4; if (CRC32C.Compute(data, 0, num) != BitConverter.ToUInt32(data, num)) { length = 0; } else { length -= 4; } } 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 += 4; } } 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); } 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) { for (int i = 0; i < length; i++) { 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) { int num = offset; int num2 = 0; while (num2 < length) { data[num] ^= _byteKey[num2 % _byteKey.Length]; num2++; num++; } } } } }