Out with MagicOnion, in with LiteNetLib

Removed MagicOnion
Added LiteNetLib
Created client and server abstractions
Fixed serialization bugs
This commit is contained in:
hyperion51 2020-03-24 14:36:22 +00:00
commit e96146b7bb
869 changed files with 59401 additions and 16812 deletions

View file

@ -0,0 +1,28 @@
using System;
using System.Collections;
using System.Collections.Generic;
public abstract class NotAnActionCollection{}
public class ActionCollection<T> : NotAnActionCollection where T : Message
{
private List<Action<T>> _actions = new List<Action<T>>();
public void Add(Action<T> action)
{
_actions.Add(action);
}
public void Invoke(T message)
{
foreach (var action in _actions)
{
action(message);
}
}
public void Clear()
{
_actions.Clear();
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 00baa3467159d704dae9afbac3f4729b
guid: 24330c4f854f8d047a47cbe0ae360b73
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -6,9 +6,10 @@ using MessagePack;
public class DatabaseCache
{
public event Action<DatabaseEntry> OnDataInsertLocal;
public event Action<DatabaseEntry> OnDataUpdateLocal;
public event Action<DatabaseEntry> OnDataUpdateRemote;
public event Action<DatabaseEntry> OnDataDeleteLocal;
public event Action<DatabaseEntry> OnDataUpdateRemote;
public Action<string> Logger = Console.WriteLine;
@ -25,6 +26,7 @@ public class DatabaseCache
{
if (entry != null)
{
var exists = _entries.ContainsKey(entry.ID);
_entries[entry.ID] = entry;
var type = entry.GetType();
var types = type.FindInterfaces((_, __) => true, null).Append(type);
@ -34,10 +36,16 @@ public class DatabaseCache
_types[t] = new Dictionary<Guid, DatabaseEntry>();
_types[t][entry.ID] = entry;
}
if(remote)
if (remote)
OnDataUpdateRemote?.Invoke(entry);
else
OnDataUpdateLocal?.Invoke(entry);
{
if (exists)
OnDataUpdateLocal?.Invoke(entry);
else
OnDataInsertLocal?.Invoke(entry);
}
}
}

View file

@ -47,13 +47,22 @@ public abstract class DatabaseEntry
[MessagePackObject]
[JsonObject(MemberSerialization.OptIn)]
public class Player : DatabaseEntry
public class Player : DatabaseEntry, INamedEntry
{
[JsonProperty("email")] [Key(1)]
public string Email;
[JsonProperty("password")] [Key(2)]
public string Password;
[JsonProperty("username")] [Key(3)]
public string Username;
[IgnoreMember] public string EntryName
{
get => Username;
set => Username = value;
}
}
[MessagePackObject]

View file

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using LiteNetLib;
using MessagePack;
using Unity.Mathematics;
using static Unity.Mathematics.math;
@ -38,6 +39,10 @@ public static class Extensions
return Quality[item];
}
public static void Send<T>(this NetPeer peer, T message, DeliveryMethod method = DeliveryMethod.ReliableOrdered) where T : Message
{
peer.Send(MessagePackSerializer.Serialize(message as Message), method);
}
public static float Performance(this EquippableItemData itemData, float temperature)
{

View file

@ -1,25 +0,0 @@
using ChatApp.Shared.MessagePackObjects;
using MagicOnion;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ChatApp.Shared.Hubs
{
/// <summary>
/// Client -> Server API (Streaming)
/// </summary>
public interface IChatHub : IStreamingHub<IChatHub, IChatHubReceiver>
{
Task JoinAsync(JoinRequest request);
Task LeaveAsync();
Task SendMessageAsync(string message);
Task GenerateException(string message);
// It is not called because it is a method as a sample of arguments.
Task SampleMethod(List<int> sampleList, Dictionary<int, string> sampleDictionary);
}
}

View file

@ -1,16 +0,0 @@
using ChatApp.Shared.MessagePackObjects;
namespace ChatApp.Shared.Hubs
{
/// <summary>
/// Server -> Client API
/// </summary>
public interface IChatHubReceiver
{
void OnJoin(string name);
void OnLeave(string name);
void OnSendMessage(MessageResponse message);
}
}

View file

@ -4,12 +4,10 @@ using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Unity.Mathematics;
using UnityEngine;
using static Unity.Mathematics.math;
public class MathJsonConverter : JsonConverter
{
private readonly Dictionary<Type, Action<JsonWriter, object>> _writers;
private readonly Dictionary<Type, Func<JArray, object>> _readers;

View file

@ -1,17 +0,0 @@
using MessagePack;
namespace ChatApp.Shared.MessagePackObjects
{
/// <summary>
/// Room participation information
/// </summary>
[MessagePackObject]
public struct JoinRequest
{
[Key(0)]
public string RoomName { get; set; }
[Key(1)]
public string UserName { get; set; }
}
}

View file

@ -1,17 +0,0 @@
using MessagePack;
namespace ChatApp.Shared.MessagePackObjects
{
/// <summary>
/// Message information
/// </summary>
[MessagePackObject]
public struct MessageResponse
{
[Key(0)]
public string UserName { get; set; }
[Key(1)]
public string Message { get; set; }
}
}

View file

@ -0,0 +1,78 @@
using System;
using System.Collections;
using System.Collections.Generic;
using LiteNetLib;
using MessagePack;
[Union(0,typeof(PingMessage))]
[Union(1,typeof(LoginMessage))]
[Union(2,typeof(RegisterMessage))]
[Union(3,typeof(VerifyMessage))]
[Union(4,typeof(ErrorMessage))]
[Union(5,typeof(LoginSuccessMessage))]
[Union(6,typeof(ChatMessage))]
[Union(7,typeof(ChatBroadcastMessage))]
[MessagePackObject]
public abstract class Message
{
[IgnoreMember] public NetPeer Peer;
}
[MessagePackObject]
public class PingMessage : Message
{
[Key(0)] public float SendTime;
}
[MessagePackObject]
public class LoginMessage : Message
{
[Key(0)] public string Email;
[Key(1)] public string Password;
}
[MessagePackObject]
public class RegisterMessage : Message
{
[Key(0)] public string Name;
[Key(1)] public string Email;
[Key(2)] public string Password;
}
[MessagePackObject]
public class VerifyMessage : Message
{
[Key(0)] public Guid Session;
}
[MessagePackObject]
public class ErrorMessage : Message
{
[Key(0)] public string Error;
}
[MessagePackObject]
public class LoginSuccessMessage : Message
{
[Key(0)] public Guid Session;
}
[MessagePackObject]
public class ChatMessage : Message
{
[Key(0)] public string Text;
}
[MessagePackObject]
public class ChatBroadcastMessage : Message
{
[Key(0)] public string User;
[Key(1)] public string Text;
}
[MessagePackObject]
public class ChangeNameMessage : Message
{
[Key(0)] public string Name;
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4857e2640847cd94f90257e00ec6854f
guid: a23b7de46296d3c4384ce28a93b1f5c0
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -25,6 +25,8 @@ namespace JsonKnownTypes
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var jo = JObject.Load(reader);
var discriminator = jo[_typesSettings.Name].ToString();

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 2e0485c6f6dee60498657a6f1f50ea69
guid: 4c6bc5e029ba5d742b1c027b638abc6c
folderAsset: yes
DefaultImporter:
externalObjects: {}

View file

@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace LiteNetLib
{
internal abstract class BaseChannel
{
public BaseChannel Next;
protected readonly NetPeer Peer;
protected readonly Queue<NetPacket> OutgoingQueue;
protected BaseChannel(NetPeer peer)
{
Peer = peer;
OutgoingQueue = new Queue<NetPacket>(64);
}
public int PacketsInQueue
{
get { return OutgoingQueue.Count; }
}
public void AddToQueue(NetPacket packet)
{
lock (OutgoingQueue)
OutgoingQueue.Enqueue(packet);
}
public abstract void SendNextPackets();
public abstract bool ProcessPacket(NetPacket packet);
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 2080c15c13ba0c14aa0682bec9550f33
guid: a3a71f45516173d48833202c65e77fe2
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

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

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ba2df8e833f170640b371a83db3df1ef
guid: 62958515af8d0f84b9f472a9a83e7bc7
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

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

View file

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

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: fab80ed56c97e934c9e7a836f31caa64
guid: e97acaef00653794eaa39be22c013509
folderAsset: yes
DefaultImporter:
externalObjects: {}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>LiteNetLib</AssemblyName>
<RootNamespace>LiteNetLib</RootNamespace>
<LangVersion>4</LangVersion>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.1;netcoreapp3.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">net35;netstandard2.0;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG</DefineConstants>
<NoWarn>1701;1702;1705;1591</NoWarn>
<LangVersion>4</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>bin\Release\net35\LiteNetLib.xml</DocumentationFile>
<NoWarn>1701;1702;1705;1591</NoWarn>
</PropertyGroup>
<Target Name="UpdateUnityDLLS" AfterTargets="CopyFilesToOutputDirectory" Condition=" '$(TargetFramework)' == 'net35' and '$(Configuration)' == 'Release' ">
<ItemGroup>
<LibraryRelease Include="$(TargetDir)LiteNetLib.dll;$(TargetDir)LiteNetLib.pdb;$(TargetDir)LiteNetLib.xml" />
</ItemGroup>
<Copy SourceFiles="@(LibraryRelease)" DestinationFolder="..\LiteNetLibSampleUnity\Assets" />
</Target>
</Project>

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3fbdb12295a729741b8e67aa4bafd989
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,239 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using LiteNetLib.Utils;
//Some code parts taken from lidgren-network-gen3
namespace LiteNetLib
{
public interface INatPunchListener
{
void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);
void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, string token);
}
public class EventBasedNatPunchListener : INatPunchListener
{
public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);
public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, string token);
public event OnNatIntroductionRequest NatIntroductionRequest;
public event OnNatIntroductionSuccess NatIntroductionSuccess;
void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token)
{
if(NatIntroductionRequest != null)
NatIntroductionRequest(localEndPoint, remoteEndPoint, token);
}
void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, string token)
{
if (NatIntroductionSuccess != null)
NatIntroductionSuccess(targetEndPoint, token);
}
}
/// <summary>
/// Module for UDP NAT Hole punching operations. Can be accessed from NetManager
/// </summary>
public sealed class NatPunchModule
{
struct RequestEventData
{
public IPEndPoint LocalEndPoint;
public IPEndPoint RemoteEndPoint;
public string Token;
}
struct SuccessEventData
{
public IPEndPoint TargetEndPoint;
public string Token;
}
private readonly NetSocket _socket;
private readonly Queue<RequestEventData> _requestEvents;
private readonly Queue<SuccessEventData> _successEvents;
private const byte HostByte = 1;
private const byte ClientByte = 0;
public const int MaxTokenLength = 256;
private INatPunchListener _natPunchListener;
internal NatPunchModule(NetSocket socket)
{
_socket = socket;
_requestEvents = new Queue<RequestEventData>();
_successEvents = new Queue<SuccessEventData>();
}
public void Init(INatPunchListener listener)
{
_natPunchListener = listener;
}
public void NatIntroduce(
IPEndPoint hostInternal,
IPEndPoint hostExternal,
IPEndPoint clientInternal,
IPEndPoint clientExternal,
string additionalInfo)
{
NetDataWriter dw = new NetDataWriter();
//First packet (server)
//send to client
dw.Put((byte)PacketProperty.NatIntroduction);
dw.Put(ClientByte);
dw.Put(hostInternal);
dw.Put(hostExternal);
dw.Put(additionalInfo, MaxTokenLength);
SocketError errorCode = 0;
_socket.SendTo(dw.Data, 0, dw.Length, clientExternal, ref errorCode);
//Second packet (client)
//send to server
dw.Reset();
dw.Put((byte)PacketProperty.NatIntroduction);
dw.Put(HostByte);
dw.Put(clientInternal);
dw.Put(clientExternal);
dw.Put(additionalInfo, MaxTokenLength);
_socket.SendTo(dw.Data, 0, dw.Length, hostExternal, ref errorCode);
}
public void PollEvents()
{
if (_natPunchListener == null)
return;
lock (_successEvents)
{
while (_successEvents.Count > 0)
{
var evt = _successEvents.Dequeue();
_natPunchListener.OnNatIntroductionSuccess(evt.TargetEndPoint, evt.Token);
}
}
lock (_requestEvents)
{
while (_requestEvents.Count > 0)
{
var evt = _requestEvents.Dequeue();
_natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token);
}
}
}
public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo)
{
//prepare outgoing data
NetDataWriter dw = new NetDataWriter();
string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4);
if (string.IsNullOrEmpty(networkIp))
{
networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6);
}
IPEndPoint localEndPoint = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort);
dw.Put((byte)PacketProperty.NatIntroductionRequest);
dw.Put(localEndPoint);
dw.Put(additionalInfo, MaxTokenLength);
//prepare packet
SocketError errorCode = 0;
_socket.SendTo(dw.Data, 0, dw.Length, masterServerEndPoint, ref errorCode);
}
private void HandleNatPunch(IPEndPoint senderEndPoint, NetDataReader dr)
{
byte fromHostByte = dr.GetByte();
if (fromHostByte != HostByte && fromHostByte != ClientByte)
{
//garbage
return;
}
//Read info
string additionalInfo = dr.GetString(MaxTokenLength);
NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}", senderEndPoint, additionalInfo);
//Release punch success to client; enabling him to Connect() to msg.Sender if token is ok
lock (_successEvents)
{
_successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Token = additionalInfo });
}
}
private void HandleNatIntroduction(NetDataReader dr)
{
// read intro
byte hostByte = dr.GetByte();
IPEndPoint remoteInternal = dr.GetNetEndPoint();
IPEndPoint remoteExternal = dr.GetNetEndPoint();
string token = dr.GetString(MaxTokenLength);
NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received; we are designated " + (hostByte == HostByte ? "host" : "client"));
NetDataWriter writer = new NetDataWriter();
// send internal punch
writer.Put((byte)PacketProperty.NatPunchMessage);
writer.Put(hostByte);
writer.Put(token);
SocketError errorCode = 0;
_socket.SendTo(writer.Data, 0, writer.Length, remoteInternal, ref errorCode);
NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + remoteInternal);
// send external punch
writer.Reset();
writer.Put((byte)PacketProperty.NatPunchMessage);
writer.Put(hostByte);
writer.Put(token);
if (hostByte == HostByte)
{
_socket.Ttl = 2;
_socket.SendTo(writer.Data, 0, writer.Length, remoteExternal, ref errorCode);
_socket.Ttl = NetConstants.SocketTTL;
}
else
{
_socket.SendTo(writer.Data, 0, writer.Length, remoteExternal, ref errorCode);
}
NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + remoteExternal);
}
private void HandleNatIntroductionRequest(IPEndPoint senderEndPoint, NetDataReader dr)
{
IPEndPoint localEp = dr.GetNetEndPoint();
string token = dr.GetString(MaxTokenLength);
lock (_requestEvents)
{
_requestEvents.Enqueue(new RequestEventData
{
LocalEndPoint = localEp,
RemoteEndPoint = senderEndPoint,
Token = token
});
}
}
internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet)
{
var dr = new NetDataReader(packet.RawData, NetConstants.HeaderSize, packet.Size);
switch (packet.Property)
{
case PacketProperty.NatIntroductionRequest:
//We got request and must introduce
HandleNatIntroductionRequest(senderEndPoint, dr);
break;
case PacketProperty.NatIntroduction:
//We got introduce and must punch
HandleNatIntroduction(dr);
break;
case PacketProperty.NatPunchMessage:
//We got punch and can connect
HandleNatPunch(senderEndPoint, dr);
break;
}
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,33 @@
using System;
namespace LiteNetLib
{
public class InvalidPacketException: ArgumentException
{
public InvalidPacketException()
{
}
public InvalidPacketException(string message): base(message)
{
}
public InvalidPacketException(string message, Exception innerException): base(message, innerException)
{
}
}
public class TooBigPacketException : InvalidPacketException
{
public TooBigPacketException()
{
}
public TooBigPacketException(string message) : base(message)
{
}
public TooBigPacketException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,243 @@
using System;
using LiteNetLib.Utils;
namespace LiteNetLib
{
internal enum PacketProperty : byte
{
Unreliable,
Channeled,
Ack,
Ping,
Pong,
ConnectRequest,
ConnectAccept,
Disconnect,
UnconnectedMessage,
NatIntroductionRequest,
NatIntroduction,
NatPunchMessage,
MtuCheck,
MtuOk,
Broadcast,
Merged,
ShutdownOk,
PeerNotFound,
InvalidProtocol
}
internal sealed class NetPacket
{
private static readonly int LastProperty = Enum.GetValues(typeof(PacketProperty)).Length;
//Header
public PacketProperty Property
{
get { return (PacketProperty)(RawData[0] & 0x1F); }
set { RawData[0] = (byte)((RawData[0] & 0xE0) | (byte)value); }
}
public byte ConnectionNumber
{
get { return (byte)((RawData[0] & 0x60) >> 5); }
set { RawData[0] = (byte) ((RawData[0] & 0x9F) | (value << 5)); }
}
public ushort Sequence
{
get { return BitConverter.ToUInt16(RawData, 1); }
set { FastBitConverter.GetBytes(RawData, 1, value); }
}
public bool IsFragmented
{
get { return (RawData[0] & 0x80) != 0; }
}
public void MarkFragmented()
{
RawData[0] |= 0x80; //set first bit
}
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); }
}
//Data
public byte[] RawData;
public ushort Size;
//Delivery
public object UserData;
public NetPacket(int size)
{
RawData = new byte[size];
Size = (ushort)size;
}
public NetPacket(PacketProperty property, int size)
{
size += GetHeaderSize(property);
RawData = new byte[size];
Property = property;
Size = (ushort)size;
}
public static int GetHeaderSize(PacketProperty property)
{
switch (property)
{
case PacketProperty.Channeled:
case PacketProperty.Ack:
return NetConstants.ChanneledHeaderSize;
case PacketProperty.Ping:
return NetConstants.HeaderSize + 2;
case PacketProperty.ConnectRequest:
return NetConnectRequestPacket.HeaderSize;
case PacketProperty.ConnectAccept:
return NetConnectAcceptPacket.Size;
case PacketProperty.Disconnect:
return NetConstants.HeaderSize + 8;
case PacketProperty.Pong:
return NetConstants.HeaderSize + 10;
default:
return NetConstants.HeaderSize;
}
}
public int GetHeaderSize()
{
return GetHeaderSize(Property);
}
//Packet contstructor from byte array
public bool FromBytes(byte[] data, int start, int packetSize)
{
//Reading property
byte property = (byte)(data[start] & 0x1F);
bool fragmented = (data[start] & 0x80) != 0;
int headerSize = GetHeaderSize((PacketProperty) property);
if (property > LastProperty || packetSize < headerSize ||
(fragmented && packetSize < headerSize + NetConstants.FragmentHeaderSize) ||
data.Length < start + packetSize)
{
return false;
}
Buffer.BlockCopy(data, start, RawData, 0, packetSize);
Size = (ushort)packetSize;
return true;
}
}
internal sealed class NetConnectRequestPacket
{
public const int HeaderSize = 13;
public readonly long ConnectionTime;
public readonly byte ConnectionNumber;
public readonly NetDataReader Data;
private NetConnectRequestPacket(long connectionId, byte connectionNumber, NetDataReader data)
{
ConnectionTime = connectionId;
ConnectionNumber = connectionNumber;
Data = data;
}
public static int GetProtocolId(NetPacket packet)
{
return BitConverter.ToInt32(packet.RawData, 1);
}
public static NetConnectRequestPacket FromData(NetPacket packet)
{
if (packet.ConnectionNumber >= NetConstants.MaxConnectionNumber)
return null;
//Getting new id for peer
long connectionId = BitConverter.ToInt64(packet.RawData, 5);
// Read data and create request
var reader = new NetDataReader(null, 0, 0);
if (packet.Size > HeaderSize)
reader.SetSource(packet.RawData, HeaderSize, packet.Size);
return new NetConnectRequestPacket(connectionId, packet.ConnectionNumber, reader);
}
public static NetPacket Make(NetDataWriter connectData, long connectId)
{
//Make initial packet
var packet = new NetPacket(PacketProperty.ConnectRequest, connectData.Length);
//Add data
FastBitConverter.GetBytes(packet.RawData, 1, NetConstants.ProtocolId);
FastBitConverter.GetBytes(packet.RawData, 5, connectId);
Buffer.BlockCopy(connectData.Data, 0, packet.RawData, HeaderSize, connectData.Length);
return packet;
}
}
internal sealed class NetConnectAcceptPacket
{
public const int Size = 11;
public readonly long ConnectionId;
public readonly byte ConnectionNumber;
public readonly bool IsReusedPeer;
private NetConnectAcceptPacket(long connectionId, byte connectionNumber, bool isReusedPeer)
{
ConnectionId = connectionId;
ConnectionNumber = connectionNumber;
IsReusedPeer = isReusedPeer;
}
public static NetConnectAcceptPacket FromData(NetPacket packet)
{
if (packet.Size > Size)
return null;
long connectionId = BitConverter.ToInt64(packet.RawData, 1);
//check connect num
byte connectionNumber = packet.RawData[9];
if (connectionNumber >= NetConstants.MaxConnectionNumber)
return null;
//check reused flag
byte isReused = packet.RawData[10];
if (isReused > 1)
return null;
return new NetConnectAcceptPacket(connectionId, connectionNumber, isReused == 1);
}
public static NetPacket Make(long connectId, byte connectNum, bool reusedPeer)
{
var packet = new NetPacket(PacketProperty.ConnectAccept, 0);
FastBitConverter.GetBytes(packet.RawData, 1, connectId);
packet.RawData[9] = connectNum;
packet.RawData[10] = (byte)(reusedPeer ? 1 : 0);
return packet;
}
}
}

View file

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

View file

@ -0,0 +1,86 @@
using System;
using System.Threading;
namespace LiteNetLib
{
internal sealed class NetPacketPool
{
private readonly NetPacket[] _pool = new NetPacket[NetConstants.PacketPoolSize];
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private int _count;
public NetPacket GetWithData(PacketProperty property, byte[] data, int start, int length)
{
int headerSize = NetPacket.GetHeaderSize(property);
NetPacket packet = GetPacket(length + headerSize);
packet.Property = property;
Buffer.BlockCopy(data, start, packet.RawData, headerSize, length);
return packet;
}
//Get packet with size
public NetPacket GetWithProperty(PacketProperty property, int size)
{
NetPacket packet = GetPacket(size + NetPacket.GetHeaderSize(property));
packet.Property = property;
return packet;
}
public NetPacket GetWithProperty(PacketProperty property)
{
NetPacket packet = GetPacket(NetPacket.GetHeaderSize(property));
packet.Property = property;
return packet;
}
public NetPacket GetPacket(int size)
{
if (size <= NetConstants.MaxPacketSize)
{
NetPacket packet = null;
_lock.EnterUpgradeableReadLock();
if (_count > 0)
{
_lock.EnterWriteLock();
_count--;
packet = _pool[_count];
_pool[_count] = null;
_lock.ExitWriteLock();
}
_lock.ExitUpgradeableReadLock();
if (packet != null)
{
packet.Size = (ushort)size;
if (packet.RawData.Length < size)
packet.RawData = new byte[size];
return packet;
}
}
return new NetPacket(size);
}
public void Recycle(NetPacket packet)
{
if (packet.RawData.Length > NetConstants.MaxPacketSize)
{
//Dont pool big packets. Save memory
return;
}
//Clean fragmented flag
packet.RawData[0] = 0;
_lock.EnterUpgradeableReadLock();
if (_count == NetConstants.PacketPoolSize)
{
_lock.ExitUpgradeableReadLock();
return;
}
_lock.EnterWriteLock();
_pool[_count] = packet;
_count++;
_lock.ExitWriteLock();
_lock.ExitUpgradeableReadLock();
}
}
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,339 @@
#if UNITY_5_3_OR_NEWER
#define UNITY
#endif
#if NETSTANDARD || NETCOREAPP
using System.Runtime.InteropServices;
#endif
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace LiteNetLib
{
internal interface INetSocketListener
{
void OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint);
}
internal sealed class NetSocket
{
public const int ReceivePollingTime = 500000; //0.5 second
private Socket _udpSocketv4;
private Socket _udpSocketv6;
private Thread _threadv4;
private Thread _threadv6;
private volatile bool _running;
private readonly INetSocketListener _listener;
private const int SioUdpConnreset = -1744830452; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse("FF02:0:0:0:0:0:0:1");
internal static readonly bool IPv6Support;
public int LocalPort { get; private set; }
public short Ttl
{
get { return _udpSocketv4.Ttl; }
set { _udpSocketv4.Ttl = value; }
}
static NetSocket()
{
#if DISABLE_IPV6 || (!UNITY_EDITOR && ENABLE_IL2CPP && !UNITY_2018_3_OR_NEWER)
IPv6Support = false;
#elif !UNITY_2019_1_OR_NEWER && !UNITY_2018_4_OR_NEWER && (!UNITY_EDITOR && ENABLE_IL2CPP && UNITY_2018_3_OR_NEWER)
string version = UnityEngine.Application.unityVersion;
IPv6Support = Socket.OSSupportsIPv6 && int.Parse(version.Remove(version.IndexOf('f')).Split('.')[2]) >= 6;
#elif UNITY_2018_2_OR_NEWER
IPv6Support = Socket.OSSupportsIPv6;
#elif UNITY
#pragma warning disable 618
IPv6Support = Socket.SupportsIPv6;
#pragma warning restore 618
#else
IPv6Support = Socket.OSSupportsIPv6;
#endif
}
public NetSocket(INetSocketListener listener)
{
_listener = listener;
}
private void ReceiveLogic(object state)
{
Socket socket = (Socket)state;
EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0);
byte[] receiveBuffer = new byte[NetConstants.MaxPacketSize];
while (_running)
{
int result;
//Reading data
try
{
if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead))
continue;
result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None,
ref bufferEndPoint);
}
catch (SocketException ex)
{
switch (ex.SocketErrorCode)
{
case SocketError.Interrupted:
case SocketError.NotSocket:
return;
case SocketError.ConnectionReset:
case SocketError.MessageSize:
case SocketError.TimedOut:
NetDebug.Write(NetLogLevel.Trace, "[R]Ignored error: {0} - {1}",
(int) ex.SocketErrorCode, ex.ToString());
break;
default:
NetDebug.WriteError("[R]Error code: {0} - {1}", (int) ex.SocketErrorCode,
ex.ToString());
_listener.OnMessageReceived(null, 0, ex.SocketErrorCode, (IPEndPoint) bufferEndPoint);
break;
}
continue;
}
catch (ObjectDisposedException)
{
return;
}
//All ok!
NetDebug.Write(NetLogLevel.Trace, "[R]Received data from {0}, result: {1}", bufferEndPoint.ToString(), result);
_listener.OnMessageReceived(receiveBuffer, result, 0, (IPEndPoint)bufferEndPoint);
}
}
public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool reuseAddress, bool ipv6)
{
_udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
if (!BindSocket(_udpSocketv4, new IPEndPoint(addressIPv4, port), reuseAddress))
return false;
LocalPort = ((IPEndPoint) _udpSocketv4.LocalEndPoint).Port;
_running = true;
_threadv4 = new Thread(ReceiveLogic);
_threadv4.Name = "SocketThreadv4(" + LocalPort + ")";
_threadv4.IsBackground = true;
_threadv4.Start(_udpSocketv4);
//Check IPv6 support
if (!IPv6Support || !ipv6)
return true;
_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
//Use one port for two sockets
if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort), reuseAddress))
{
try
{
#if !UNITY
_udpSocketv6.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.AddMembership,
new IPv6MulticastOption(MulticastAddressV6));
#endif
}
catch(Exception)
{
// Unity3d throws exception - ignored
}
_threadv6 = new Thread(ReceiveLogic);
_threadv6.Name = "SocketThreadv6(" + LocalPort + ")";
_threadv6.IsBackground = true;
_threadv6.Start(_udpSocketv6);
}
return true;
}
private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress)
{
//Setup socket
socket.ReceiveTimeout = 500;
socket.SendTimeout = 500;
socket.ReceiveBufferSize = NetConstants.SocketBufferSize;
socket.SendBufferSize = NetConstants.SocketBufferSize;
#if !UNITY || UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
#if NETSTANDARD || NETCOREAPP
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#endif
try
{
socket.IOControl(SioUdpConnreset, new byte[] {0}, null);
}
catch
{
//ignored
}
#endif
try
{
socket.ExclusiveAddressUse = !reuseAddress;
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress);
}
catch
{
//Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it
}
if (socket.AddressFamily == AddressFamily.InterNetwork)
{
socket.Ttl = NetConstants.SocketTTL;
#if NETSTANDARD || NETCOREAPP
if(!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
#endif
try { socket.DontFragment = true; }
catch (SocketException e)
{
NetDebug.WriteError("[B]DontFragment error: {0}", e.SocketErrorCode);
}
try { socket.EnableBroadcast = true; }
catch (SocketException e)
{
NetDebug.WriteError("[B]Broadcast error: {0}", e.SocketErrorCode);
}
}
//Bind
try
{
socket.Bind(ep);
NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port);
}
catch (SocketException bindException)
{
switch (bindException.SocketErrorCode)
{
//IPv6 bind fix
case SocketError.AddressAlreadyInUse:
if (socket.AddressFamily == AddressFamily.InterNetworkV6)
{
try
{
socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, true);
socket.Bind(ep);
}
catch (SocketException ex)
{
NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", ex.ToString(), ex.SocketErrorCode);
return false;
}
return true;
}
break;
//hack for iOS (Unity3D)
case SocketError.AddressFamilyNotSupported:
return true;
}
NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", bindException.ToString(), bindException.SocketErrorCode);
return false;
}
return true;
}
public bool SendBroadcast(byte[] data, int offset, int size, int port)
{
bool broadcastSuccess = false;
bool multicastSuccess = false;
try
{
broadcastSuccess = _udpSocketv4.SendTo(
data,
offset,
size,
SocketFlags.None,
new IPEndPoint(IPAddress.Broadcast, port)) > 0;
if (_udpSocketv6 != null)
{
multicastSuccess = _udpSocketv6.SendTo(
data,
offset,
size,
SocketFlags.None,
new IPEndPoint(MulticastAddressV6, port)) > 0;
}
}
catch (Exception ex)
{
NetDebug.WriteError("[S][MCAST]" + ex);
return broadcastSuccess;
}
return broadcastSuccess || multicastSuccess;
}
public int SendTo(byte[] data, int offset, int size, IPEndPoint remoteEndPoint, ref SocketError errorCode)
{
try
{
var socket = _udpSocketv4;
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support)
socket = _udpSocketv6;
int result = socket.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint);
NetDebug.Write(NetLogLevel.Trace, "[S]Send packet to {0}, result: {1}", remoteEndPoint, result);
return result;
}
catch (SocketException ex)
{
switch (ex.SocketErrorCode)
{
case SocketError.NoBufferSpaceAvailable:
case SocketError.Interrupted:
return 0;
case SocketError.MessageSize: //do nothing
break;
default:
NetDebug.WriteError("[S]" + ex);
break;
}
errorCode = ex.SocketErrorCode;
return -1;
}
catch (Exception ex)
{
NetDebug.WriteError("[S]" + ex);
return -1;
}
}
public void Close()
{
_running = false;
// first close sockets
if (_udpSocketv4 != null)
{
_udpSocketv4.Close();
_udpSocketv4 = null;
}
if (_udpSocketv6 != null)
{
_udpSocketv6.Close();
_udpSocketv6 = null;
}
// then join threads
if (_threadv4 != null)
{
if (_threadv4 != Thread.CurrentThread)
_threadv4.Join();
_threadv4 = null;
}
if (_threadv6 != null)
{
if (_threadv6 != Thread.CurrentThread)
_threadv6.Join();
_threadv6 = null;
}
}
}
}

View file

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

View file

@ -0,0 +1,39 @@
namespace LiteNetLib
{
public sealed class NetStatistics
{
public ulong PacketsSent;
public ulong PacketsReceived;
public ulong BytesSent;
public ulong BytesReceived;
public ulong PacketLoss;
public ulong PacketLossPercent
{
get { return PacketsSent == 0 ? 0 : PacketLoss * 100 / PacketsSent; }
}
public ulong SequencedPacketLoss;
public void Reset()
{
PacketsSent = 0;
PacketsReceived = 0;
BytesSent = 0;
BytesReceived = 0;
PacketLoss = 0;
}
public override string ToString()
{
return
string.Format(
"BytesReceived: {0}\nPacketsReceived: {1}\nBytesSent: {2}\nPacketsSent: {3}\nPacketLoss: {4}\nPacketLossPercent: {5}\n",
BytesReceived,
PacketsReceived,
BytesSent,
PacketsSent,
PacketLoss,
PacketLossPercent);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,93 @@
using System;
namespace LiteNetLib
{
internal sealed class SequencedChannel : BaseChannel
{
private int _localSequence;
private ushort _remoteSequence;
private readonly bool _reliable;
private NetPacket _lastPacket;
private readonly NetPacket _ackPacket;
private bool _mustSendAck;
private readonly byte _id;
private long _lastPacketSendTime;
public SequencedChannel(NetPeer peer, bool reliable, byte id) : base(peer)
{
_id = id;
_reliable = reliable;
if (_reliable)
_ackPacket = new NetPacket(PacketProperty.Ack, 0) {ChannelId = id};
}
public override void SendNextPackets()
{
if (_reliable && OutgoingQueue.Count == 0)
{
long currentTime = DateTime.UtcNow.Ticks;
long packetHoldTime = currentTime - _lastPacketSendTime;
if (packetHoldTime < Peer.ResendDelay * TimeSpan.TicksPerMillisecond)
return;
var packet = _lastPacket;
if (packet != null)
{
_lastPacketSendTime = currentTime;
Peer.SendUserData(packet);
}
}
else
{
lock (OutgoingQueue)
{
while (OutgoingQueue.Count > 0)
{
NetPacket packet = OutgoingQueue.Dequeue();
_localSequence = (_localSequence + 1) % NetConstants.MaxSequence;
packet.Sequence = (ushort)_localSequence;
packet.ChannelId = _id;
Peer.SendUserData(packet);
if (_reliable && OutgoingQueue.Count == 0)
{
_lastPacketSendTime = DateTime.UtcNow.Ticks;
_lastPacket = packet;
}
else
{
Peer.NetManager.NetPacketPool.Recycle(packet);
}
}
}
}
if (_reliable && _mustSendAck)
{
_mustSendAck = false;
_ackPacket.Sequence = _remoteSequence;
Peer.SendUserData(_ackPacket);
}
}
public override bool ProcessPacket(NetPacket packet)
{
if (packet.Property == PacketProperty.Ack)
{
if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Sequence)
_lastPacket = null;
return false;
}
int relative = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence);
bool packetProcessed = false;
if (packet.Sequence < NetConstants.MaxSequence && relative > 0)
{
Peer.Statistics.PacketLoss += (ulong)(relative - 1);
_remoteSequence = packet.Sequence;
Peer.AddIncomingPacket(packet);
packetProcessed = true;
}
_mustSendAck = true;
return packetProcessed;
}
}
}

View file

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

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 3dfb982d047b6c348836f49863993e1d
guid: 60daddd56681bec4a8d826c66bb9463b
folderAsset: yes
DefaultImporter:
externalObjects: {}

View file

@ -0,0 +1,102 @@
#if NETCOREAPP3_0
using System;
using System.Runtime.Intrinsics.X86;
#endif
namespace LiteNetLib.Utils
{
//Implemenatation from Crc32.NET
public static class CRC32C
{
#if NETCOREAPP3_0
private static bool _x64Available;
private static bool _sseAvailable;
#endif
public const int ChecksumSize = 4;
private const uint Poly = 0x82F63B78u;
private static readonly uint[] Table;
static CRC32C()
{
#if NETCOREAPP3_0
_sseAvailable = Sse42.IsSupported;
_x64Available = Sse42.X64.IsSupported;
if(_sseAvailable)
return;
#endif
Table = new uint[16 * 256];
for (uint i = 0; i < 256; i++)
{
uint res = i;
for (int t = 0; t < 16; t++)
{
for (int k = 0; k < 8; k++)
res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1);
Table[t * 256 + i] = res;
}
}
}
/// <summary>
/// Compute CRC32C for data
/// </summary>
/// <param name="input">input data</param>
/// <param name="offset">offset</param>
/// <param name="length">length</param>
/// <returns>CRC32C checksum</returns>
public static uint Compute(byte[] input, int offset, int length)
{
uint crcLocal = uint.MaxValue;
#if NETCOREAPP3_0
if(_sseAvailable)
{
if (_x64Available)
{
while (length >= 8)
{
crcLocal = (uint)Sse42.X64.Crc32(crcLocal, BitConverter.ToUInt64(input, offset));
offset += 8;
length -= 8;
}
}
while (length > 0)
{
crcLocal = Sse42.Crc32(crcLocal, input[offset]);
offset++;
length--;
}
return crcLocal ^ uint.MaxValue;
}
#endif
while (length >= 16)
{
var a = Table[(3 * 256) + input[offset + 12]]
^ Table[(2 * 256) + input[offset + 13]]
^ Table[(1 * 256) + input[offset + 14]]
^ Table[(0 * 256) + input[offset + 15]];
var b = Table[(7 * 256) + input[offset + 8]]
^ Table[(6 * 256) + input[offset + 9]]
^ Table[(5 * 256) + input[offset + 10]]
^ Table[(4 * 256) + input[offset + 11]];
var c = Table[(11 * 256) + input[offset + 4]]
^ Table[(10 * 256) + input[offset + 5]]
^ Table[(9 * 256) + input[offset + 6]]
^ Table[(8 * 256) + input[offset + 7]];
var d = Table[(15 * 256) + ((byte)crcLocal ^ input[offset])]
^ Table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])]
^ Table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])]
^ Table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])];
crcLocal = d ^ c ^ b ^ a;
offset += 16;
length -= 16;
}
while (--length >= 0)
crcLocal = Table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8;
return crcLocal ^ uint.MaxValue;
}
}
}

View file

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

View file

@ -0,0 +1,118 @@
using System.Runtime.InteropServices;
namespace LiteNetLib.Utils
{
public static class FastBitConverter
{
[StructLayout(LayoutKind.Explicit)]
private struct ConverterHelperDouble
{
[FieldOffset(0)]
public ulong Along;
[FieldOffset(0)]
public double Adouble;
}
[StructLayout(LayoutKind.Explicit)]
private struct ConverterHelperFloat
{
[FieldOffset(0)]
public int Aint;
[FieldOffset(0)]
public float Afloat;
}
private static void WriteLittleEndian(byte[] buffer, int offset, ulong data)
{
#if BIGENDIAN
buffer[offset + 7] = (byte)(data);
buffer[offset + 6] = (byte)(data >> 8);
buffer[offset + 5] = (byte)(data >> 16);
buffer[offset + 4] = (byte)(data >> 24);
buffer[offset + 3] = (byte)(data >> 32);
buffer[offset + 2] = (byte)(data >> 40);
buffer[offset + 1] = (byte)(data >> 48);
buffer[offset ] = (byte)(data >> 56);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
buffer[offset + 2] = (byte)(data >> 16);
buffer[offset + 3] = (byte)(data >> 24);
buffer[offset + 4] = (byte)(data >> 32);
buffer[offset + 5] = (byte)(data >> 40);
buffer[offset + 6] = (byte)(data >> 48);
buffer[offset + 7] = (byte)(data >> 56);
#endif
}
private static void WriteLittleEndian(byte[] buffer, int offset, int data)
{
#if BIGENDIAN
buffer[offset + 3] = (byte)(data);
buffer[offset + 2] = (byte)(data >> 8);
buffer[offset + 1] = (byte)(data >> 16);
buffer[offset ] = (byte)(data >> 24);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
buffer[offset + 2] = (byte)(data >> 16);
buffer[offset + 3] = (byte)(data >> 24);
#endif
}
public static void WriteLittleEndian(byte[] buffer, int offset, short data)
{
#if BIGENDIAN
buffer[offset + 1] = (byte)(data);
buffer[offset ] = (byte)(data >> 8);
#else
buffer[offset] = (byte)(data);
buffer[offset + 1] = (byte)(data >> 8);
#endif
}
public static void GetBytes(byte[] bytes, int startIndex, double value)
{
ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value };
WriteLittleEndian(bytes, startIndex, ch.Along);
}
public static void GetBytes(byte[] bytes, int startIndex, float value)
{
ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value };
WriteLittleEndian(bytes, startIndex, ch.Aint);
}
public static void GetBytes(byte[] bytes, int startIndex, short value)
{
WriteLittleEndian(bytes, startIndex, value);
}
public static void GetBytes(byte[] bytes, int startIndex, ushort value)
{
WriteLittleEndian(bytes, startIndex, (short)value);
}
public static void GetBytes(byte[] bytes, int startIndex, int value)
{
WriteLittleEndian(bytes, startIndex, value);
}
public static void GetBytes(byte[] bytes, int startIndex, uint value)
{
WriteLittleEndian(bytes, startIndex, (int)value);
}
public static void GetBytes(byte[] bytes, int startIndex, long value)
{
WriteLittleEndian(bytes, startIndex, (ulong)value);
}
public static void GetBytes(byte[] bytes, int startIndex, ulong value)
{
WriteLittleEndian(bytes, startIndex, value);
}
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,707 @@
using System;
using System.Net;
using System.Text;
namespace LiteNetLib.Utils
{
public class NetDataReader
{
protected byte[] _data;
protected int _position;
protected int _dataSize;
private int _offset;
public byte[] RawData
{
get { return _data; }
}
public int RawDataSize
{
get { return _dataSize; }
}
public int UserDataOffset
{
get { return _offset; }
}
public int UserDataSize
{
get { return _dataSize - _offset; }
}
public bool IsNull
{
get { return _data == null; }
}
public int Position
{
get { return _position; }
}
public bool EndOfData
{
get { return _position == _dataSize; }
}
public int AvailableBytes
{
get { return _dataSize - _position; }
}
public void SkipBytes(int count)
{
_position += count;
}
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)
{
_data = source;
_position = offset;
_offset = offset;
_dataSize = source.Length;
}
public void SetSource(byte[] source, int offset, int maxSize)
{
_data = source;
_position = offset;
_offset = offset;
_dataSize = maxSize;
}
public NetDataReader()
{
}
public NetDataReader(byte[] source)
{
SetSource(source);
}
public NetDataReader(byte[] source, int offset)
{
SetSource(source, offset);
}
public NetDataReader(byte[] source, int offset, int maxSize)
{
SetSource(source, offset, maxSize);
}
#region GetMethods
public IPEndPoint GetNetEndPoint()
{
string host = GetString(1000);
int port = GetInt();
return NetUtils.MakeEndPoint(host, port);
}
public byte GetByte()
{
byte res = _data[_position];
_position += 1;
return res;
}
public sbyte GetSByte()
{
var b = (sbyte)_data[_position];
_position++;
return b;
}
public bool[] GetBoolArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new bool[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetBool();
}
return arr;
}
public ushort[] GetUShortArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new ushort[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetUShort();
}
return arr;
}
public short[] GetShortArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new short[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetShort();
}
return arr;
}
public long[] GetLongArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new long[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetLong();
}
return arr;
}
public ulong[] GetULongArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new ulong[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetULong();
}
return arr;
}
public int[] GetIntArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new int[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetInt();
}
return arr;
}
public uint[] GetUIntArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new uint[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetUInt();
}
return arr;
}
public float[] GetFloatArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new float[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetFloat();
}
return arr;
}
public double[] GetDoubleArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new double[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetDouble();
}
return arr;
}
public string[] GetStringArray()
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new string[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetString();
}
return arr;
}
public string[] GetStringArray(int maxStringLength)
{
ushort size = BitConverter.ToUInt16(_data, _position);
_position += 2;
var arr = new string[size];
for (int i = 0; i < size; i++)
{
arr[i] = GetString(maxStringLength);
}
return arr;
}
public bool GetBool()
{
bool res = _data[_position] > 0;
_position += 1;
return res;
}
public char GetChar()
{
char result = BitConverter.ToChar(_data, _position);
_position += 2;
return result;
}
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)
{
int bytesCount = GetInt();
if (bytesCount <= 0 || bytesCount > maxLength*2)
{
return string.Empty;
}
int charCount = Encoding.UTF8.GetCharCount(_data, _position, bytesCount);
if (charCount > maxLength)
{
return string.Empty;
}
string result = Encoding.UTF8.GetString(_data, _position, bytesCount);
_position += bytesCount;
return result;
}
public string GetString()
{
int bytesCount = GetInt();
if (bytesCount <= 0)
{
return string.Empty;
}
string result = Encoding.UTF8.GetString(_data, _position, bytesCount);
_position += bytesCount;
return result;
}
public ArraySegment<byte> GetRemainingBytesSegment()
{
ArraySegment<byte> segment = new ArraySegment<byte>(_data, _position, AvailableBytes);
_position = _data.Length;
return segment;
}
public T Get<T>() where T : INetSerializable, new()
{
var obj = new T();
obj.Deserialize(this);
return obj;
}
public byte[] GetRemainingBytes()
{
byte[] outgoingData = new byte[AvailableBytes];
Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes);
_position = _data.Length;
return outgoingData;
}
public void GetBytes(byte[] destination, int start, int count)
{
Buffer.BlockCopy(_data, _position, destination, start, count);
_position += count;
}
public void GetBytes(byte[] destination, int count)
{
Buffer.BlockCopy(_data, _position, destination, 0, count);
_position += count;
}
public sbyte[] GetSBytesWithLength()
{
int length = GetInt();
sbyte[] outgoingData = new sbyte[length];
Buffer.BlockCopy(_data, _position, outgoingData, 0, length);
_position += length;
return outgoingData;
}
public byte[] GetBytesWithLength()
{
int length = GetInt();
byte[] outgoingData = new byte[length];
Buffer.BlockCopy(_data, _position, outgoingData, 0, length);
_position += length;
return outgoingData;
}
#endregion
#region PeekMethods
public byte PeekByte()
{
return _data[_position];
}
public sbyte PeekSByte()
{
return (sbyte)_data[_position];
}
public bool PeekBool()
{
return _data[_position] > 0;
}
public char PeekChar()
{
return BitConverter.ToChar(_data, _position);
}
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)
{
int bytesCount = BitConverter.ToInt32(_data, _position);
if (bytesCount <= 0 || bytesCount > maxLength * 2)
{
return string.Empty;
}
int charCount = Encoding.UTF8.GetCharCount(_data, _position + 4, bytesCount);
if (charCount > maxLength)
{
return string.Empty;
}
string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount);
return result;
}
public string PeekString()
{
int bytesCount = BitConverter.ToInt32(_data, _position);
if (bytesCount <= 0)
{
return string.Empty;
}
string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount);
return result;
}
#endregion
#region TryGetMethods
public bool TryGetByte(out byte result)
{
if (AvailableBytes >= 1)
{
result = GetByte();
return true;
}
result = 0;
return false;
}
public bool TryGetSByte(out sbyte result)
{
if (AvailableBytes >= 1)
{
result = GetSByte();
return true;
}
result = 0;
return false;
}
public bool TryGetBool(out bool result)
{
if (AvailableBytes >= 1)
{
result = GetBool();
return true;
}
result = false;
return false;
}
public bool TryGetChar(out char result)
{
if (AvailableBytes >= 2)
{
result = GetChar();
return true;
}
result = '\0';
return false;
}
public bool TryGetShort(out short result)
{
if (AvailableBytes >= 2)
{
result = GetShort();
return true;
}
result = 0;
return false;
}
public bool TryGetUShort(out ushort result)
{
if (AvailableBytes >= 2)
{
result = GetUShort();
return true;
}
result = 0;
return false;
}
public bool TryGetInt(out int result)
{
if (AvailableBytes >= 4)
{
result = GetInt();
return true;
}
result = 0;
return false;
}
public bool TryGetUInt(out uint result)
{
if (AvailableBytes >= 4)
{
result = GetUInt();
return true;
}
result = 0;
return false;
}
public bool TryGetLong(out long result)
{
if (AvailableBytes >= 8)
{
result = GetLong();
return true;
}
result = 0;
return false;
}
public bool TryGetULong(out ulong result)
{
if (AvailableBytes >= 8)
{
result = GetULong();
return true;
}
result = 0;
return false;
}
public bool TryGetFloat(out float result)
{
if (AvailableBytes >= 4)
{
result = GetFloat();
return true;
}
result = 0;
return false;
}
public bool TryGetDouble(out double result)
{
if (AvailableBytes >= 8)
{
result = GetDouble();
return true;
}
result = 0;
return false;
}
public bool TryGetString(out string result)
{
if (AvailableBytes >= 4)
{
var bytesCount = PeekInt();
if (AvailableBytes >= bytesCount + 4)
{
result = GetString();
return true;
}
}
result = null;
return false;
}
public bool TryGetStringArray(out string[] result)
{
ushort size;
if (!TryGetUShort(out size))
{
result = null;
return false;
}
result = new string[size];
for (int i = 0; i < size; i++)
{
if (!TryGetString(out result[i]))
{
result = null;
return false;
}
}
return true;
}
public bool TryGetBytesWithLength(out byte[] result)
{
if (AvailableBytes >= 4)
{
var length = PeekInt();
if (length >= 0 && AvailableBytes >= length + 4)
{
result = GetBytesWithLength();
return true;
}
}
result = null;
return false;
}
#endregion
public void Clear()
{
_position = 0;
_dataSize = 0;
_data = null;
}
}
}

View file

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

View file

@ -0,0 +1,415 @@
using System;
using System.Net;
using System.Text;
namespace LiteNetLib.Utils
{
public class NetDataWriter
{
protected byte[] _data;
protected int _position;
private const int InitialSize = 64;
private readonly bool _autoResize;
public int Capacity
{
get { return _data.Length; }
}
public NetDataWriter() : this(true, InitialSize)
{
}
public NetDataWriter(bool autoResize) : this(autoResize, InitialSize)
{
}
public NetDataWriter(bool autoResize, int initialSize)
{
_data = new byte[initialSize];
_autoResize = autoResize;
}
/// <summary>
/// Creates NetDataWriter from existing ByteArray
/// </summary>
/// <param name="bytes">Source byte array</param>
/// <param name="copy">Copy array to new location or use existing</param>
public static NetDataWriter FromBytes(byte[] bytes, bool copy)
{
if (copy)
{
var netDataWriter = new NetDataWriter(true, bytes.Length);
netDataWriter.Put(bytes);
return netDataWriter;
}
return new NetDataWriter(true, 0) {_data = bytes};
}
/// <summary>
/// Creates NetDataWriter from existing ByteArray (always copied data)
/// </summary>
/// <param name="bytes">Source byte array</param>
/// <param name="offset">Offset of array</param>
/// <param name="length">Length of array</param>
public static NetDataWriter FromBytes(byte[] bytes, int offset, int length)
{
var netDataWriter = new NetDataWriter(true, bytes.Length);
netDataWriter.Put(bytes, offset, length);
return netDataWriter;
}
public static NetDataWriter FromString(string value)
{
var netDataWriter = new NetDataWriter();
netDataWriter.Put(value);
return netDataWriter;
}
public void ResizeIfNeed(int newSize)
{
int len = _data.Length;
if (len < newSize)
{
while (len < newSize)
len *= 2;
Array.Resize(ref _data, len);
}
}
public void Reset(int size)
{
ResizeIfNeed(size);
_position = 0;
}
public void Reset()
{
_position = 0;
}
public byte[] CopyData()
{
byte[] resultData = new byte[_position];
Buffer.BlockCopy(_data, 0, resultData, 0, _position);
return resultData;
}
public byte[] Data
{
get { return _data; }
}
public int Length
{
get { return _position; }
}
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)
{
if (_autoResize)
ResizeIfNeed(_position + 2);
FastBitConverter.GetBytes(_data, _position, value);
_position += 2;
}
public void Put(ushort value)
{
if (_autoResize)
ResizeIfNeed(_position + 2);
FastBitConverter.GetBytes(_data, _position, value);
_position += 2;
}
public void Put(short value)
{
if (_autoResize)
ResizeIfNeed(_position + 2);
FastBitConverter.GetBytes(_data, _position, value);
_position += 2;
}
public void Put(sbyte value)
{
if (_autoResize)
ResizeIfNeed(_position + 1);
_data[_position] = (byte)value;
_position++;
}
public void Put(byte value)
{
if (_autoResize)
ResizeIfNeed(_position + 1);
_data[_position] = value;
_position++;
}
public void Put(byte[] data, int offset, int length)
{
if (_autoResize)
ResizeIfNeed(_position + length);
Buffer.BlockCopy(data, offset, _data, _position, length);
_position += length;
}
public void Put(byte[] data)
{
if (_autoResize)
ResizeIfNeed(_position + data.Length);
Buffer.BlockCopy(data, 0, _data, _position, data.Length);
_position += data.Length;
}
public void PutSBytesWithLength(sbyte[] data, int offset, int length)
{
if (_autoResize)
ResizeIfNeed(_position + length + 4);
FastBitConverter.GetBytes(_data, _position, length);
Buffer.BlockCopy(data, offset, _data, _position + 4, length);
_position += length + 4;
}
public void PutSBytesWithLength(sbyte[] data)
{
if (_autoResize)
ResizeIfNeed(_position + data.Length + 4);
FastBitConverter.GetBytes(_data, _position, data.Length);
Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length);
_position += data.Length + 4;
}
public void PutBytesWithLength(byte[] data, int offset, int length)
{
if (_autoResize)
ResizeIfNeed(_position + length + 4);
FastBitConverter.GetBytes(_data, _position, length);
Buffer.BlockCopy(data, offset, _data, _position + 4, length);
_position += length + 4;
}
public void PutBytesWithLength(byte[] data)
{
if (_autoResize)
ResizeIfNeed(_position + data.Length + 4);
FastBitConverter.GetBytes(_data, _position, data.Length);
Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length);
_position += data.Length + 4;
}
public void Put(bool value)
{
if (_autoResize)
ResizeIfNeed(_position + 1);
_data[_position] = (byte)(value ? 1 : 0);
_position++;
}
public void PutArray(float[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 4 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(double[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 8 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(long[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 8 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(ulong[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 8 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(int[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 4 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(uint[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 4 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(ushort[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 2 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(short[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len * 2 + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(bool[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
if (_autoResize)
ResizeIfNeed(_position + len + 2);
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(string[] value)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
Put(len);
for (int i = 0; i < len; i++)
Put(value[i]);
}
public void PutArray(string[] value, int maxLength)
{
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
Put(len);
for (int i = 0; i < len; i++)
Put(value[i], maxLength);
}
public void Put(IPEndPoint endPoint)
{
Put(endPoint.Address.ToString());
Put(endPoint.Port);
}
public void Put(string value)
{
if (string.IsNullOrEmpty(value))
{
Put(0);
return;
}
//put bytes count
int bytesCount = Encoding.UTF8.GetByteCount(value);
if (_autoResize)
ResizeIfNeed(_position + bytesCount + 4);
Put(bytesCount);
//put string
Encoding.UTF8.GetBytes(value, 0, value.Length, _data, _position);
_position += bytesCount;
}
public void Put(string value, int maxLength)
{
if (string.IsNullOrEmpty(value))
{
Put(0);
return;
}
int length = value.Length > maxLength ? maxLength : value.Length;
//calculate max count
int bytesCount = Encoding.UTF8.GetByteCount(value);
if (_autoResize)
ResizeIfNeed(_position + bytesCount + 4);
//put bytes count
Put(bytesCount);
//put string
Encoding.UTF8.GetBytes(value, 0, length, _data, _position);
_position += bytesCount;
}
public void Put<T>(T obj) where T : INetSerializable
{
obj.Serialize(this);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,142 @@
using System;
using System.Net;
using System.Net.Sockets;
namespace LiteNetLib.Utils
{
/// <summary>
/// Make NTP request.
/// <para>
/// 1. Create the object by <see cref="Create(IPEndPoint,Action&lt;NtpPacket&gt;)"/> method.
/// </para>
/// <para>
/// 2. Use <see cref="Send"/> method to send requests. 3. Call <see cref="Close"/> to release the socket
/// AFTER you have received the response or some timeout. If you close the socket too early, you may miss the response.
/// </para>
/// <para>
/// 3. Call <see cref="Close"/> to release the socket AFTER you have received the response or some timeout.
/// If you close the socket too early, you may miss the response.
/// </para>
/// </summary>
public sealed class NtpRequest : INetSocketListener
{
public const int DefaultPort = 123;
private readonly NetSocket _socket;
private readonly Action<NtpPacket> _onRequestComplete;
private readonly IPEndPoint _ntpEndPoint;
/// <summary>
/// Initialize object, open socket.
/// </summary>
/// <param name="endPoint">NTP Server endpoint</param>
/// <param name="onRequestComplete">callback (called from other thread!)</param>
private NtpRequest(IPEndPoint endPoint, Action<NtpPacket> onRequestComplete)
{
_ntpEndPoint = endPoint;
_onRequestComplete = onRequestComplete;
// Create and start socket
_socket = new NetSocket(this);
_socket.Bind(IPAddress.Any, IPAddress.IPv6Any, 0, false, endPoint.AddressFamily == AddressFamily.InterNetworkV6);
}
/// <summary>
/// Create the requests for NTP server, open socket.
/// </summary>
/// <param name="endPoint">NTP Server address.</param>
/// <param name="onRequestComplete">callback (called from other thread!)</param>
public static NtpRequest Create(IPEndPoint endPoint, Action<NtpPacket> onRequestComplete)
{
return new NtpRequest(endPoint, onRequestComplete);
}
/// <summary>
/// Create the requests for NTP server (default port), open socket.
/// </summary>
/// <param name="ipAddress">NTP Server address.</param>
/// <param name="onRequestComplete">callback (called from other thread!)</param>
public static NtpRequest Create(IPAddress ipAddress, Action<NtpPacket> onRequestComplete)
{
IPEndPoint endPoint = new IPEndPoint(ipAddress, DefaultPort);
return Create(endPoint, onRequestComplete);
}
/// <summary>
/// Create the requests for NTP server, open socket.
/// </summary>
/// <param name="ntpServerAddress">NTP Server address.</param>
/// <param name="port">port</param>
/// <param name="onRequestComplete">callback (called from other thread!)</param>
public static NtpRequest Create(string ntpServerAddress, int port, Action<NtpPacket> onRequestComplete)
{
IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, port);
return Create(endPoint, onRequestComplete);
}
/// <summary>
/// Create the requests for NTP server (default port), open socket.
/// </summary>
/// <param name="ntpServerAddress">NTP Server address.</param>
/// <param name="onRequestComplete">callback (called from other thread!)</param>
public static NtpRequest Create(string ntpServerAddress, Action<NtpPacket> onRequestComplete)
{
IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, DefaultPort);
return Create(endPoint, onRequestComplete);
}
/// <summary>
/// Send request to the NTP server calls callback (if success).
/// In case of error the callbacke is called with null param.
/// </summary>
public void Send()
{
SocketError errorCode = 0;
var packet = new NtpPacket();
packet.ValidateRequest(); // not necessary
byte[] sendData = packet.Bytes;
var sendCount = _socket.SendTo(sendData, 0, sendData.Length, _ntpEndPoint, ref errorCode);
if (errorCode != 0 || sendCount != sendData.Length)
{
_onRequestComplete(null);
}
}
/// <summary>
/// Close socket.
/// </summary>
public void Close()
{
_socket.Close();
}
/// <summary>
/// Handle received data: transform bytes to NtpPacket, close socket and call the callback.
/// </summary>
void INetSocketListener.OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint)
{
DateTime destinationTimestamp = DateTime.UtcNow;
if (!remoteEndPoint.Equals(_ntpEndPoint))
return;
if (length < 48)
{
NetDebug.Write(NetLogLevel.Trace, "NTP response too short: {}", length);
_onRequestComplete(null);
return;
}
NtpPacket packet = NtpPacket.FromServerResponse(data, destinationTimestamp);
try
{
packet.ValidateReply();
}
catch (InvalidOperationException ex)
{
NetDebug.Write(NetLogLevel.Trace, "NTP response error: {}", ex.Message);
packet = null;
}
_onRequestComplete(packet);
}
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
using System;
namespace RethinkDb.Driver.Linq.Attributes
{
public class PrimaryIndexAttribute : Attribute
{
}
}

View file

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

View file

@ -0,0 +1,8 @@
using System;
namespace RethinkDb.Driver.Linq.Attributes
{
public class SecondaryIndexAttribute : Attribute
{
}
}

View file

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

View file

@ -0,0 +1,26 @@
using Remotion.Linq;
using Remotion.Linq.Parsing.Structure;
using RethinkDb.Driver.Ast;
using RethinkDb.Driver.Net;
namespace RethinkDb.Driver.Linq
{
public static class LinqExtensions
{
public static RethinkQueryable<T> AsQueryable<T>( this Table term, IConnection conn )
{
var executor = new RethinkQueryExecutor( term, conn );
return new RethinkQueryable<T>(
new DefaultQueryProvider(
typeof( RethinkQueryable<> ),
QueryParser.CreateDefault(),
executor )
);
}
public static RethinkQueryable<T> Table<T>( this Db db, string tableName, IConnection conn )
{
return db.Table( tableName ).AsQueryable<T>( conn );
}
}
}

View file

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

View file

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Remotion.Linq.Clauses.Expressions;
using RethinkDb.Driver.Ast;
using RethinkDb.Driver.Linq.WhereClauseParsers.SubQueryVisitor;
using RethinkDb.Driver.Utils;
namespace RethinkDb.Driver.Linq
{
public class MemberNameResolver
{
private readonly MemberExpression _expression;
public MemberNameResolver( MemberExpression expression )
{
_expression = expression;
}
public ReqlExpr Resolve( ReqlExpr reqlExpr ) => ResolveMemberExpression( reqlExpr, _expression );
private static ReqlExpr ResolveMemberExpression( ReqlExpr reqlExpr, MemberExpression expression )
{
if( expression.Expression.NodeType == ExpressionType.MemberAccess )
reqlExpr = ResolveMemberExpression( reqlExpr, (MemberExpression)expression.Expression );
if( expression.Expression.NodeType == ExpressionType.Extension && expression.Expression is SubQueryExpression )
reqlExpr = ResolveExtensionExpression( reqlExpr, (SubQueryExpression)expression.Expression );
return reqlExpr[QueryHelper.GetJsonMemberName( expression.Member )];
}
private static ReqlExpr ResolveExtensionExpression( ReqlExpr reqlExpr, SubQueryExpression expression )
{
var subQueryVisitors = new List<ISubQueryVisitor>
{
new AnySubQueryVisitor(),
new AllSubQueryVisitor(),
new FirstAndLastSubQueryVisitor()
};
var subQueryVisitor = subQueryVisitors.FirstOrDefault( x => x.CanVisit( expression.QueryModel ) );
if( subQueryVisitor == null )
throw new NotSupportedException( "subqueries not allowed ." );
return subQueryVisitor.Visit( reqlExpr, expression.QueryModel );
}
}
}

View file

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

View file

@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>An experimental LINQ to ReQL provider for the RethinkDB C# Driver.</Description>
<PackageReleaseNotes>
</PackageReleaseNotes>
<Version>0.0.0-localbuild</Version>
<Authors>Brian Chavez;jrote1</Authors>
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
<CodeAnalysisRuleSet>RethinkDb.Driver.Linq.ruleset</CodeAnalysisRuleSet>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<SignAssembly>false</SignAssembly>
<AssemblyName>RethinkDb.Driver.Linq</AssemblyName>
<PackageTags>rethinkdb;document;database;driver;reql;linq;nosql</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/wiki/bchavez/RethinkDb.Driver/logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/bchavez/RethinkDb.Driver</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/bchavez/RethinkDb.Driver/master/LICENSE</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/bchavez/RethinkDb.Driver</RepositoryUrl>
<!--Source Link Settings-->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>false</EmbedUntrackedSources>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);STANDARD</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RethinkDb.Driver\RethinkDb.Driver.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Remotion.Linq" Version="2.1.1" />
<PackageReference Include="Roslynator.Analyzers" Version="1.5.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63127-02" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<PackageReference Include="Common.Logging" Version="3.3.0" />
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>

View file

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

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Default" Description="Default" ToolsVersion="14.0">
<Rules AnalyzerId="Roslynator.CSharp.Analyzers" RuleNamespace="Roslynator.CSharp.Analyzers">
<Rule Id="RCS1090" Action="Error" />
</Rules>
</RuleSet>

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4a29586679b0ef3489a21ca519dd6c41
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace RethinkDb.Driver.Linq
{
public class RethinkDbGroup<T, TVal> : IGrouping<T, TVal>
{
public IEnumerator<TVal> GetEnumerator()
{
return Reduction.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public T Key { get; set; }
public List<TVal> Reduction { get; set; }
}
}

View file

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

View file

@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Remotion.Linq;
using Remotion.Linq.Clauses;
using Remotion.Linq.Clauses.Expressions;
using Remotion.Linq.Clauses.ResultOperators;
using RethinkDb.Driver.Ast;
using RethinkDb.Driver.Linq.Attributes;
using RethinkDb.Driver.Linq.WhereClauseParsers;
using ExpressionVisitor = RethinkDb.Driver.Linq.WhereClauseParsers.ExpressionVisitor;
using RethinkDb.Driver.Utils;
namespace RethinkDb.Driver.Linq
{
public class RethinkDbQueryModelVisitor : QueryModelVisitorBase
{
private readonly Table _table;
public ReqlAst Query => Stack.Peek();
public Stack<ReqlExpr> Stack = new Stack<ReqlExpr>();
public RethinkDbQueryModelVisitor( Table table )
{
_table = table;
}
public override void VisitQueryModel( QueryModel queryModel )
{
Stack.Push( _table );
base.VisitQueryModel( queryModel );
}
public override void VisitMainFromClause( MainFromClause fromClause, QueryModel queryModel )
{
var subQuery = fromClause.FromExpression as SubQueryExpression;
if( subQuery != null )
{
var query = subQuery.QueryModel;
VisitBodyClauses( query.BodyClauses, query );
base.VisitMainFromClause( fromClause, queryModel );
}
base.VisitMainFromClause( fromClause, queryModel );
}
public override void VisitWhereClause( WhereClause whereClause, QueryModel queryModel, int index )
{
var expressions = SplitWhereClause( whereClause.Predicate );
foreach( var expression in expressions.ToList() )
{
var whereClauseParsers = new List<IWhereClauseParser>
{
new GroupItemsParser(),
new PrimaryIndexParser(),
new SecondaryIndexParser(),
new DefaultParser()
};
var reql = Stack.Pop();
var matchingParser = whereClauseParsers.FirstOrDefault( x => x.IsAppropriate( reql, expression, queryModel.ResultTypeOverride ) );
if( matchingParser != null )
Stack.Push( matchingParser.Parse( reql, queryModel, expression ) );
else
throw new NotSupportedException( "Unable to vist Where clause" );
}
}
private static IEnumerable<Expression> SplitWhereClause( Expression predicate )
{
if( predicate.NodeType != ExpressionType.AndAlso )
{
yield return predicate;
yield break;
}
var binaryExpression = predicate as BinaryExpression;
if( binaryExpression == null )
yield break;
foreach( var expression in SplitWhereClause( binaryExpression.Left ) )
yield return expression;
yield return binaryExpression.Right;
}
private static ReqlExpr GetSelectReqlAst( Expression selector )
{
var visitor = new SelectionProjector();
visitor.Visit( selector );
return visitor.Current;
}
public override void VisitOrderByClause( OrderByClause orderByClause, QueryModel queryModel, int index )
{
var expression = orderByClause.Orderings[0].Expression as MemberExpression;
if( expression == null )
return;
OrderBy reql;
var currentStack = Stack.Pop();
var memberName = QueryHelper.GetJsonMemberName( expression.Member );
if( orderByClause.Orderings[0].OrderingDirection == OrderingDirection.Asc )
reql = currentStack.OrderBy( memberName );
else
reql = currentStack.OrderBy( RethinkDB.R.Desc( memberName ) );
if( currentStack is Table && expression.Member.CustomAttributes.Any( x => x.AttributeType == typeof( PrimaryIndexAttribute ) || x.AttributeType == typeof( SecondaryIndexAttribute ) ) )
reql = reql.OptArg( "index", memberName );
Stack.Push( reql );
}
public override void VisitSelectClause( SelectClause selectClause, QueryModel queryModel )
{
if( queryModel.ResultOperators.FirstOrDefault() is AverageResultOperator )
{
var memberExpression = selectClause.Selector as MemberExpression;
var memberNameResolver = new MemberNameResolver( memberExpression );
Stack.Push( Stack.Pop().Avg( x => memberNameResolver.Resolve( x ) ) );
return;
}
if( queryModel.ResultOperators.FirstOrDefault() is CountResultOperator )
{
Stack.Push( Stack.Pop().Count( ) );
return;
}
var expr = GetSelectReqlAst( selectClause.Selector );
if( !ReferenceEquals( expr, null ) )
{
}
base.VisitSelectClause( selectClause, queryModel );
}
protected override void VisitBodyClauses( ObservableCollection<IBodyClause> bodyClauses, QueryModel queryModel )
{
if( queryModel.ResultTypeOverride.GetTypeInfo().IsGenericTypeDefinition && queryModel.ResultTypeOverride.GetGenericArguments()[0].GetGenericTypeDefinition() != typeof( IGrouping<,> ) )
{
base.VisitBodyClauses( bodyClauses, queryModel );
return;
}
var group = queryModel.ResultOperators.FirstOrDefault() as GroupResultOperator;
if( group == null )
{
base.VisitBodyClauses( bodyClauses, queryModel );
return;
}
var keySelector = (MemberExpression)group.KeySelector;
var groupReql = Stack.Pop().Group( QueryHelper.GetJsonMemberName( keySelector.Member ) );
var memberAccess = group.ElementSelector as MemberExpression;
Stack.Push( memberAccess != null ? groupReql.GetField( QueryHelper.GetJsonMemberName( memberAccess.Member ) ).Ungroup() : groupReql.Ungroup() );
}
public override void VisitResultOperator( ResultOperatorBase resultOperator, QueryModel queryModel, int index )
{
if( resultOperator is AnyResultOperator )
Stack.Push( Stack.Pop().Count() );
else if( resultOperator is AllResultOperator )
{
var allResultOperator = resultOperator as AllResultOperator;
Stack.Push( Stack.Pop().Filter( x => GetWhereReqlAst( x, allResultOperator.Predicate ).Not() ).Count() );
}
else if( resultOperator is FirstResultOperator )
Stack.Push( Stack.Pop().Nth( 0 ) );
else if( resultOperator is LastResultOperator )
Stack.Push( Stack.Pop().Nth( -1 ) );
base.VisitResultOperator( resultOperator, queryModel, index );
}
private static ReqlExpr GetWhereReqlAst( ReqlExpr reqlExpr, Expression predicate )
{
var visitor = new ExpressionVisitor( reqlExpr, typeof( bool ) );
visitor.Visit( predicate );
return visitor.Current;
}
}
}

View file

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

View file

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Remotion.Linq;
using Remotion.Linq.Clauses.ResultOperators;
using RethinkDb.Driver.Ast;
using RethinkDb.Driver.Net;
namespace RethinkDb.Driver.Linq
{
public class RethinkQueryExecutor : IQueryExecutor
{
private readonly Table table;
private readonly IConnection connection;
public RethinkQueryExecutor( Table table, IConnection connection )
{
this.table = table;
this.connection = connection;
}
protected virtual void ProcessQuery( ReqlAst query )
{
}
public T ExecuteScalar<T>( QueryModel queryModel )
{
var visitor = new RethinkDbQueryModelVisitor( table );
visitor.VisitQueryModel( queryModel );
var query = visitor.Query;
ProcessQuery( query );
var result = query.Run( connection );
if( queryModel.ResultOperators.FirstOrDefault() is AnyResultOperator )
return result > 0;
if( queryModel.ResultOperators.FirstOrDefault() is AllResultOperator )
return result == 0;
return (T)result;
}
public T ExecuteSingle<T>( QueryModel queryModel, bool returnDefaultWhenEmpty )
{
var visitor = new RethinkDbQueryModelVisitor( table );
visitor.VisitQueryModel( queryModel );
var query = visitor.Query;
ProcessQuery( query );
try
{
return query.RunResult<T>( connection );
}
catch( ReqlNonExistenceError ex )
{
if( ShouldReturnDefault( queryModel ) )
return default( T );
throw new InvalidOperationException( ex.Message );
}
}
private static bool ShouldReturnDefault( QueryModel queryModel )
{
var firstResultOperator = queryModel.ResultOperators.FirstOrDefault();
return ( firstResultOperator as FirstResultOperator )?.ReturnDefaultWhenEmpty
?? ( firstResultOperator as LastResultOperator )?.ReturnDefaultWhenEmpty
?? false;
}
public IEnumerable<T> ExecuteCollection<T>( QueryModel queryModel )
{
var visitor = new RethinkDbQueryModelVisitor( table );
visitor.VisitQueryModel( queryModel );
var query = visitor.Query;
ProcessQuery( query );
if( typeof(T).GetTypeInfo().IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(IGrouping<,>) )
{
return typeof(RethinkQueryExecutor)
.GetMethod(nameof(DeserializeGrouping),
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)
.MakeGenericMethod(
typeof(T).GetGenericArguments()[0],
typeof(T).GetGenericArguments()[1])
.Invoke(null, new object[]
{
query.Run(connection) as JArray
}) as IEnumerable<T>;
}
if( query is Get )
{
return new List<T>
{
query.RunResult<T>(connection)
};
}
return query.RunResult<List<T>>( connection );
}
private static List<IGrouping<T, TVal>> DeserializeGrouping<T, TVal>( JArray groups )
{
var result = new List<IGrouping<T, TVal>>();
foreach( var group in groups )
{
var groupArray = (JObject)group;
result.Add( new RethinkDbGroup<T, TVal>
{
Key = groupArray["group"].ToObject<T>(),
Reduction = JsonConvert.DeserializeObject<List<TVal>>( groupArray["reduction"].ToString() )
} );
}
return result;
}
}
}

View file

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

View file

@ -0,0 +1,22 @@
using System.Linq;
using System.Linq.Expressions;
using Remotion.Linq;
using Remotion.Linq.Parsing.Structure;
namespace RethinkDb.Driver.Linq
{
public class RethinkQueryable<T> : QueryableBase<T>
{
public RethinkQueryable( IQueryParser queryParser, IQueryExecutor executor ) : base( queryParser, executor )
{
}
public RethinkQueryable( IQueryProvider provider ) : base( provider )
{
}
public RethinkQueryable( IQueryProvider provider, Expression expression ) : base( provider, expression )
{
}
}
}

View file

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

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Remotion.Linq.Clauses.Expressions;
using Remotion.Linq.Parsing;
using RethinkDb.Driver.Ast;
namespace RethinkDb.Driver.Linq
{
public class SelectionProjector : ThrowingExpressionVisitor
{
private readonly Stack<ReqlExpr> _stack = new Stack<ReqlExpr>();
public ReqlExpr Current => _stack.Count > 0 ? _stack.Peek() : null;
protected override Exception CreateUnhandledItemException<T>( T unhandledItem, string visitMethod )
{
throw new NotImplementedException();
}
protected override Expression VisitQuerySourceReference( QuerySourceReferenceExpression expression )
{
return expression;
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,40 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using Remotion.Linq;
using RethinkDb.Driver.Ast;
namespace RethinkDb.Driver.Linq.WhereClauseParsers
{
public abstract class BaseIndexParser<T> : IWhereClauseParser
{
public bool IsAppropriate( ReqlAst reql, Expression expression, Type resultType )
{
var binaryExpression = expression as BinaryExpression;
if( !( reql is Table ) || binaryExpression?.NodeType != ExpressionType.Equal )
return false;
var left = binaryExpression.Left as MemberExpression;
if( left != null )
return IsIndex( left );
var right = binaryExpression.Right as MemberExpression;
return right != null && IsIndex( right );
}
private static bool IsIndex( MemberExpression left )
{
return left.Member.CustomAttributes.Any( x => x.AttributeType == typeof( T ) );
}
public abstract ReqlExpr Parse( ReqlExpr expression, QueryModel queryModel, Expression predicate );
protected static object GetValue( BinaryExpression binaryExpression )
{
var left = binaryExpression.Left as ConstantExpression;
return left != null ? left.Value : ( (ConstantExpression)binaryExpression.Right ).Value;
}
}
}

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