using System; using System.Collections.Generic; namespace LiteNetLib.Utils { public class NetPacketProcessor { private static class HashCache { public static bool Initialized; public static ulong Id; } protected delegate void SubscribeDelegate(NetDataReader reader, object userData); private readonly NetSerializer _netSerializer; private readonly Dictionary _callbacks = new Dictionary(); 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() { if(HashCache.Initialized) return HashCache.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.Initialized = true; HashCache.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(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 Send(NetPeer peer, T packet, DeliveryMethod options) where T : class, new() { _netDataWriter.Reset(); Write(_netDataWriter, packet); peer.Send(_netDataWriter, options); } public void SendNetSerializable(NetPeer peer, T packet, DeliveryMethod options) where T : INetSerializable { _netDataWriter.Reset(); WriteNetSerializable(_netDataWriter, packet); peer.Send(_netDataWriter, options); } public void Send(NetManager manager, T packet, DeliveryMethod options) where T : class, new() { _netDataWriter.Reset(); Write(_netDataWriter, packet); manager.SendToAll(_netDataWriter, options); } public void SendNetSerializable(NetManager manager, T packet, DeliveryMethod options) where T : INetSerializable { _netDataWriter.Reset(); WriteNetSerializable(_netDataWriter, packet); manager.SendToAll(_netDataWriter, options); } public void Write(NetDataWriter writer, T packet) where T : class, new() { WriteHash(writer); _netSerializer.Serialize(writer, packet); } public void WriteNetSerializable(NetDataWriter writer, T packet) where T : INetSerializable { WriteHash(writer); packet.Serialize(writer); } public byte[] Write(T packet) where T : class, new() { _netDataWriter.Reset(); WriteHash(_netDataWriter); _netSerializer.Serialize(_netDataWriter, packet); return _netDataWriter.CopyData(); } public byte[] WriteNetSerializable(T packet) where T : INetSerializable { _netDataWriter.Reset(); WriteHash(_netDataWriter); packet.Serialize(_netDataWriter); return _netDataWriter.CopyData(); } /// /// 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(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(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(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(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()); } } }