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 OnEnteringUniverse(IUniverse universe) { universe.OnPreUpdate += PollEvents; } protected override void OnExitingUniverse(IUniverse universe) { universe.OnPreUpdate -= PollEvents; Stop(); } private void PollEvents(IUniverse sender, UniverseTime engineTime) => Manager.PollEvents(); protected virtual void OnPacketArrived(T packet, NetPeer peer) where T : INetworkPacket { if (!listeners.TryGetValue(typeof(T), out List? delegates)) return; foreach (Delegate @delegate in delegates) @delegate.InvokeSafe(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); MethodInfo subscribeReusableMethod = typeof(NetPacketProcessor) .GetMethods() .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) )!; MethodInfo onPacketArrivedMethod = typeof(LiteNetLibCommunicatorBase) .GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance)!; // Register all network packets by calling the method bellow where T is our found network packet type // NetPacketProcessor.SubscribeReusable(Action onReceive) foreach (var packetType in packetTypes) { var genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType); var delegateType = typeof(Action<,>).MakeGenericType(packetType, typeof(NetPeer)); var 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 void SubscribeToPackets(Action callback) { if (!listeners.TryGetValue(typeof(T), out List? delegates)) { delegates = []; listeners.Add(typeof(T), delegates); } delegates.Add(callback); } public void UnsubscribeFromPackets(Action callback) { if (!listeners.TryGetValue(typeof(T), out List? delegates)) return; delegates.Remove(callback); } }