using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using LiteNetLib; using LiteNetLib.Utils; using Syntriax.Engine.Core; namespace Syntriax.Engine.Network; public abstract class LiteNetLibCommunicatorBase : UniverseObject, INetworkCommunicator { protected readonly NetPacketProcessor netPacketProcessor = new(); private readonly Dictionary> listeners = []; public EventBasedNetListener Listener { get; private set; } = null!; public NetManager Manager { get; private set; } = null!; public INetworkCommunicator Stop() { Manager.Stop(); return this; } protected override void OnExitingUniverse(IUniverse universe) { base.OnExitingUniverse(universe); Stop(); } protected virtual void OnPacketArrived(T packet, NetPeer peer) where T : INetworkPacket { if (!listeners.TryGetValue(typeof(T), out Event? @event)) return; @event.Invoke(peer.Id.ToString(), packet); } private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) { try { netPacketProcessor.ReadAllPackets(reader, peer); } catch { } } private void SetupPackets() { // Find network packets implementing INetworkPacket IEnumerable packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) .Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType); MethodInfo subscribeReusableMethod = typeof(NetPacketProcessor) .GetMethods() .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) )!; MethodInfo registerNestedTypeMethod = typeof(NetPacketProcessor) .GetMethods() .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.RegisterNestedType) && m.GetParameters().Length == 0 )!; MethodInfo onPacketArrivedMethod = typeof(LiteNetLibCommunicatorBase) .GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance)!; // Register all network packets by calling the methods bellow where T is our found network packet type // NetPacketProcessor.SubscribeReusable(Action onReceive) // NetPacketProcessor.RegisterNestedType() foreach (Type packetType in packetTypes) { if (!packetType.IsClass) { MethodInfo genericRegisterNestedTypeMethod = registerNestedTypeMethod.MakeGenericMethod(packetType); genericRegisterNestedTypeMethod.Invoke(netPacketProcessor, []); continue; } MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType); Type delegateType = typeof(Action<,>).MakeGenericType(packetType, typeof(NetPeer)); Delegate delegateHandler = Delegate.CreateDelegate(delegateType, this, genericOnPacketArrivedMethod); var genericSubscribeReusableMethod = subscribeReusableMethod.MakeGenericMethod(packetType, typeof(NetPeer)); genericSubscribeReusableMethod.Invoke(netPacketProcessor, [delegateHandler]); } } public LiteNetLibCommunicatorBase() { Listener = new EventBasedNetListener(); Manager = new NetManager(Listener); Listener.NetworkReceiveEvent += NetworkReceiveEvent; SetupEnginePackets(); SetupPackets(); } private void SetupEnginePackets() { // I know, ugly af. I need to find a better way netPacketProcessor.RegisterNestedType(AABBNetPacker.Write, AABBNetPacker.Read); netPacketProcessor.RegisterNestedType(CircleNetPacker.Write, CircleNetPacker.Read); netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.Read); netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read); netPacketProcessor.RegisterNestedType(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read); netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read); netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.Read); netPacketProcessor.RegisterNestedType(Projection1DNetPacker.Write, Projection1DNetPacker.Read); netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.Read); netPacketProcessor.RegisterNestedType(Shape2DNetPacker.Write, Shape2DNetPacker.Read); netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read); netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read); netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.Read); } public INetworkCommunicator SubscribeToPackets(Event.EventHandler callback) { Type type = typeof(T); if (!listeners.TryGetValue(type, out Event? @event)) { @event = new(); listeners.Add(type, @event); } @event.AddListener(EventDelegateWrapper(callback)); return this; } public INetworkCommunicator UnsubscribeFromPackets(Event.EventHandler callback) { Type type = typeof(T); if (!listeners.TryGetValue(type, out Event? @event)) return this; @event.RemoveListener(EventDelegateWrapper(callback)); return this; } private static Event.EventHandler EventDelegateWrapper(Event.EventHandler callback) => (sender, @object) => callback.Invoke(sender, (T)@object); }