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