diff --git a/Engine.Systems/Network/NetworkManager.cs b/Engine.Systems/Network/NetworkManager.cs index 4474997..2847efe 100644 --- a/Engine.Systems/Network/NetworkManager.cs +++ b/Engine.Systems/Network/NetworkManager.cs @@ -10,35 +10,127 @@ namespace Engine.Systems.Network; /// /// Intermediary manager that looks up in it's hierarchy for a to route/broadcast it's received packets to their destinations. /// -/// TODO: I urgently need to add proper comments on this manager, I don't exactly remember the state I was in when I was writing it. +/// TODO: I need to peer check this class, I don't exactly remember the state I was in when I was originally writing it and left it uncommented and the current comments are added later on. /// It's a fairly complex manager that relies heavily on Reflection and lots of generic method delegation which is making it very hard to read back. public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetworkManager { - private readonly Dictionary>> clientBroadcastPacketArrivalMethods = []; - private readonly Dictionary>> serverBroadcastPacketArrivalMethods = []; + #region Packet Router/Broadcaster to Listener Delegates - private readonly Dictionary> clientBroadcastPacketRouters = []; - private readonly Dictionary> serverBroadcastPacketRouters = []; + /// + /// Behaviour Type → Packet Type → List of listener methods (broadcast packets, client-side) + /// + private readonly Dictionary>> clientBroadcastPacketListenerMethods = []; - private readonly Dictionary>> clientEntityPacketArrivalMethods = []; - private readonly Dictionary>> serverEntityPacketArrivalMethods = []; + /// + /// Behaviour Type → Packet Type → List of listener methods (broadcast packets, server-side) + /// + private readonly Dictionary>> serverBroadcastPacketListenerMethods = []; - private readonly Dictionary> clientEntityPacketRouters = []; - private readonly Dictionary> serverEntityPacketRouters = []; + /// + /// Behaviour Type → Packet Type → List of listener methods (entity packets, client-side) + /// + private readonly Dictionary>> clientEntityPacketListenerMethods = []; - private readonly List<(Type packetType, Delegate @delegate)> broadcastPacketRetrievalDelegates = []; - private readonly List<(Type packetType, Delegate @delegate)> entityPacketRetrievalDelegates = []; + /// + /// Behaviour Type → Packet Type → List of listener methods (entity packets, server-side) + /// + private readonly Dictionary>> serverEntityPacketListenerMethods = []; + #endregion + + #region Packet Router/Broadcaster Events + + /// + /// Packet Type → Behaviour.Id → Broadcaster Event (broadcast, client-side) + /// + private readonly Dictionary> clientPacketBroadcastEvents = []; + + /// + /// Packet Type → Behaviour.Id → Broadcaster Event (broadcast, server-side) + /// + private readonly Dictionary> serverPacketBroadcastEvents = []; + + /// + /// Maps an type to a set of routing events, + /// keyed by , for CLIENT entity listeners. + /// The packet is routed to the correct instance + /// by matching . + /// + private readonly Dictionary> clientEntityPacketRouterEvents = []; + + /// + /// Maps an type to a set of routing events, + /// keyed by , for SERVER entity listeners. + /// The packet is routed to the correct instance + /// by matching . + /// + private readonly Dictionary> serverEntityPacketRouterEvents = []; + + #endregion + + #region Packet Retrieval Delegates + + /// + /// Stores delegates that connect incoming broadcast packets from + /// to . + /// These are used to subscribe/unsubscribe from events. + /// + private readonly List broadcastPacketRetrievalSubscriptionDelegates = []; + + /// + /// Stores delegates that connect incoming entity packets from + /// to . + /// These are used to subscribe/unsubscribe from events. + /// + private readonly List entityPacketRetrievalSubscriptionDelegates = []; + + /// + /// Stores delegates that connect incoming all packets from to + /// . This is a combination of all subscription + /// delegates filtered so there are no duplicates packet entries. + /// + private readonly List uniqueRetrievalSubscriptionDelegates = []; + + #endregion + + #region Method Caches + + /// + /// Packet type → method. + /// private readonly Dictionary clearRoutesMethods = []; + + /// + /// Packet type → method. + /// private readonly Dictionary registerBroadcastPacketListenersMethods = []; + + /// + /// Packet type → method. + /// private readonly Dictionary registerEntityPacketListenersMethods = []; + #endregion + + #region Network Entity Collector + + /// + /// All active network , keyed by . + /// private readonly Dictionary _networkEntities = []; public IReadOnlyDictionary NetworkEntities => _networkEntities; + /// + /// Collector responsible for detecting s entering/leaving the universe. + /// private readonly BehaviourCollector _networkEntityCollector = new(); + public IBehaviourCollector NetworkEntityCollector => _networkEntityCollector; + #endregion + + #region Network Communicator + public INetworkCommunicator NetworkCommunicator { get; @@ -50,34 +142,46 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork INetworkCommunicator? previousCommunicator = field; field = value; - if (previousCommunicator is not null) InvokeCommunicatorMethods(previousCommunicator, nameof(INetworkCommunicator.UnsubscribeFromPackets)); - if (field is not null) InvokeCommunicatorMethods(field, nameof(INetworkCommunicator.SubscribeToPackets)); + // Unsubscribe packet delegates from old communicator + if (previousCommunicator is not null) + InvokeCommunicatorMethods(nameof(INetworkCommunicator.UnsubscribeFromPackets), previousCommunicator); + + // Subscribe packet delegates to new communicator + if (field is not null) + InvokeCommunicatorMethods(nameof(INetworkCommunicator.SubscribeToPackets), field); } } = null!; - /// Used to delegate subscription and unsubscription methods on the to . - private void InvokeCommunicatorMethods(INetworkCommunicator networkCommunicator, string name) + /// + /// Dynamically invokes + /// or + /// on the provided for all known packet types. + /// + private void InvokeCommunicatorMethods(string methodName, INetworkCommunicator networkCommunicator) { MethodInfo unsubscribeFromPacketsMethod = typeof(INetworkCommunicator) - .GetMethod(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!; + .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!; - /// Get unique entries by the packetType so we don't get duplicate calls to - /// Because a class might have both and - /// or and together. - IEnumerable<(Type packetType, Delegate @delegate)> distinctRetrievalSubscriptionDelegates = - broadcastPacketRetrievalDelegates.Concat(entityPacketRetrievalDelegates).DistinctBy(pair => pair.packetType); - - foreach ((Type packetType, Delegate @delegate) in distinctRetrievalSubscriptionDelegates) + foreach ((Type packetType, Delegate @delegate) in uniqueRetrievalSubscriptionDelegates) { MethodInfo genericMethod = unsubscribeFromPacketsMethod.MakeGenericMethod(packetType); genericMethod.Invoke(networkCommunicator, [@delegate]); } } + #endregion + + //////////////////////////////////////////////////////////////// + #region Packet Routing/Broadcasting + + /// + /// Entry point for ALL incoming packets from the . + /// private void OnPacketReceived(IConnection sender, T entityDataPacket) { BroadcastPacket(sender, entityDataPacket); + if (entityDataPacket is IEntityNetworkPacket entityPacket) RoutePacket(sender, entityDataPacket, entityPacket); } @@ -85,25 +189,27 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork private void RoutePacket(IConnection sender, T entityDataPacket, IEntityNetworkPacket entityPacket) { if (NetworkCommunicator is INetworkCommunicatorClient) - RoutePacket(clientEntityPacketRouters, entityPacket.EntityId, sender, entityDataPacket); + RoutePacket(clientEntityPacketRouterEvents, entityPacket.EntityId, sender, entityDataPacket); + if (NetworkCommunicator is INetworkCommunicatorServer) - RoutePacket(serverEntityPacketRouters, entityPacket.EntityId, sender, entityDataPacket); + RoutePacket(serverEntityPacketRouterEvents, entityPacket.EntityId, sender, entityDataPacket); } private void BroadcastPacket(IConnection sender, T entityDataPacket) { if (NetworkCommunicator is INetworkCommunicatorClient) - BroadcastPacket(clientBroadcastPacketRouters, sender, entityDataPacket); + BroadcastPacket(clientPacketBroadcastEvents, sender, entityDataPacket); + if (NetworkCommunicator is INetworkCommunicatorServer) - BroadcastPacket(serverBroadcastPacketRouters, sender, entityDataPacket); + BroadcastPacket(serverPacketBroadcastEvents, sender, entityDataPacket); } private void BroadcastPacket( - Dictionary> packetRouters, + Dictionary> packetBroadcasters, IConnection sender, T entityDataPacket) { - if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary? routers)) + if (!packetBroadcasters.TryGetValue(entityDataPacket!.GetType(), out Dictionary? routers)) return; foreach ((string behaviourId, object routerEventReference) in routers) @@ -128,19 +234,25 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork Event routerEvent = (Event)routerEventReference; routerEvent.Invoke(sender, entityDataPacket!); } + #endregion #region Packet Routers + + /// + /// Registers routing events for the behaviour based on cached packet listener methods. + /// private void RegisterPacketRoutersFor( INetworkEntity behaviour, Dictionary> packetRouters, - Dictionary>> packetArrivalMethods, - NetworkType networkType, Dictionary registerPacketListenersMethods) + Dictionary>> packetListenerMethods, + NetworkType networkType, + Dictionary registerPacketListenerListenersMethods) { - if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary>? arrivalMethods)) + if (!packetListenerMethods.TryGetValue(behaviour.GetType(), out Dictionary>? listenerMethods)) return; - foreach (Type packetType in arrivalMethods.Keys) + foreach (Type packetType in listenerMethods.Keys) { if (!packetRouters.TryGetValue(packetType, out Dictionary? routers)) { @@ -148,14 +260,20 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork packetRouters.Add(packetType, routers); } - object packetListenerEvent = - CreateEventAndRegister(packetType, behaviour, networkType, registerPacketListenersMethods); + object packetListenerEvent = CreateEventAndRegister(packetType, behaviour, networkType, registerPacketListenerListenersMethods); routers.Add(behaviour.Id, packetListenerEvent); } } - private object CreateEventAndRegister(Type packetType, INetworkEntity behaviour, NetworkType networkType, Dictionary registerPacketListenersMethods) + /// + /// Creates an Event and attaches listener callbacks. + /// + private object CreateEventAndRegister( + Type packetType, + INetworkEntity behaviour, + NetworkType networkType, + Dictionary registerPacketListenersMethods) { Type genericEventType = typeof(Event<,>).MakeGenericType(typeof(IConnection), packetType); object packetListenerEvent = Activator.CreateInstance(genericEventType)!; @@ -167,18 +285,31 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork return packetListenerEvent; } - private static void RegisterPacketListenerEvent( + /// + /// Registers broadcast packet listeners on the behaviour. + /// + private static void RegisterBroadcastPacketListenerEvent( INetworkEntity behaviour, Event packetListenerEvent, NetworkType networkType) { switch (networkType) { - case NetworkType.Client: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerClient)behaviour).OnClientPacketArrived(sender, packet)); break; - case NetworkType.Server: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerServer)behaviour).OnServerPacketArrived(sender, packet)); break; + case NetworkType.Client: + packetListenerEvent.AddListener( + (sender, packet) => ((IPacketListenerClient)behaviour).OnClientPacketArrived(sender, packet)); + break; + + case NetworkType.Server: + packetListenerEvent.AddListener( + (sender, packet) => ((IPacketListenerServer)behaviour).OnServerPacketArrived(sender, packet)); + break; } } + /// + /// Registers entity-specific packet listeners on a network behaviour. + /// private static void RegisterEntityPacketListenerEvent( INetworkEntity behaviour, Event packetListenerEvent, @@ -187,20 +318,30 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork { switch (networkType) { - case NetworkType.Client: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerClientEntity)behaviour).OnEntityClientPacketArrived(sender, packet)); break; - case NetworkType.Server: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerServerEntity)behaviour).OnEntityServerPacketArrived(sender, packet)); break; + case NetworkType.Client: + packetListenerEvent.AddListener( + (sender, packet) => ((IPacketListenerClientEntity)behaviour).OnEntityClientPacketArrived(sender, packet)); + break; + + case NetworkType.Server: + packetListenerEvent.AddListener( + (sender, packet) => ((IPacketListenerServerEntity)behaviour).OnEntityServerPacketArrived(sender, packet)); + break; } } + /// + /// Unregisters all routing events associated with the behaviour. + /// private void UnregisterPacketRoutersFor( INetworkEntity behaviour, Dictionary> packetRouters, - Dictionary>> packetArrivalMethods) + Dictionary>> packetListenerMethods) { - if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary>? arrivalMethods)) + if (!packetListenerMethods.TryGetValue(behaviour.GetType(), out Dictionary>? listenerMethods)) return; - foreach ((Type packetType, List methods) in arrivalMethods) + foreach ((Type packetType, List methods) in listenerMethods) { if (!packetRouters.TryGetValue(packetType, out Dictionary? routers)) continue; @@ -215,6 +356,9 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork } } + /// + /// Clears all listeners from a router event. + /// private static void ClearRouter(object routerEventReference) { Event routerEvent = (Event)routerEventReference; @@ -224,133 +368,192 @@ public class NetworkManager : Behaviour, IEnterUniverse, IExitUniverse, INetwork #endregion #region Engine Callbacks - private void OnCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args) + + /// + /// Called when an enters the universe. + /// Registers all packet routing for that entity. + /// + private void OnCollected( + IBehaviourCollector sender, + IBehaviourCollector.BehaviourCollectedArguments args) { INetworkEntity collectedBehaviour = args.BehaviourCollected; if (!_networkEntities.TryAdd(collectedBehaviour.Id, collectedBehaviour)) throw new($"Unable to add {collectedBehaviour.Id} to {nameof(NetworkManager)}"); - RegisterPacketRoutersFor(collectedBehaviour, clientBroadcastPacketRouters, clientBroadcastPacketArrivalMethods, NetworkType.Client, registerBroadcastPacketListenersMethods); - RegisterPacketRoutersFor(collectedBehaviour, clientEntityPacketRouters, clientEntityPacketArrivalMethods, NetworkType.Client, registerEntityPacketListenersMethods); + RegisterPacketRoutersFor(collectedBehaviour, clientPacketBroadcastEvents, clientBroadcastPacketListenerMethods, NetworkType.Client, registerBroadcastPacketListenersMethods); + RegisterPacketRoutersFor(collectedBehaviour, clientEntityPacketRouterEvents, clientEntityPacketListenerMethods, NetworkType.Client, registerEntityPacketListenersMethods); - RegisterPacketRoutersFor(collectedBehaviour, serverBroadcastPacketRouters, serverBroadcastPacketArrivalMethods, NetworkType.Server, registerBroadcastPacketListenersMethods); - RegisterPacketRoutersFor(collectedBehaviour, serverEntityPacketRouters, serverEntityPacketArrivalMethods, NetworkType.Server, registerEntityPacketListenersMethods); + RegisterPacketRoutersFor(collectedBehaviour, serverPacketBroadcastEvents, serverBroadcastPacketListenerMethods, NetworkType.Server, registerBroadcastPacketListenersMethods); + RegisterPacketRoutersFor(collectedBehaviour, serverEntityPacketRouterEvents, serverEntityPacketListenerMethods, NetworkType.Server, registerEntityPacketListenersMethods); } + /// + /// Called when an is removed from the universe. + /// Cleans up all routing. + /// private void OnRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args) { INetworkEntity removedBehaviour = args.BehaviourRemoved; - if (!_networkEntities.Remove(args.BehaviourRemoved.Id)) + + if (!_networkEntities.Remove(removedBehaviour.Id)) return; - UnregisterPacketRoutersFor(removedBehaviour, clientBroadcastPacketRouters, clientBroadcastPacketArrivalMethods); - UnregisterPacketRoutersFor(removedBehaviour, clientEntityPacketRouters, clientEntityPacketArrivalMethods); + UnregisterPacketRoutersFor(removedBehaviour, clientPacketBroadcastEvents, clientBroadcastPacketListenerMethods); + UnregisterPacketRoutersFor(removedBehaviour, clientEntityPacketRouterEvents, clientEntityPacketListenerMethods); - UnregisterPacketRoutersFor(removedBehaviour, serverBroadcastPacketRouters, serverBroadcastPacketArrivalMethods); - UnregisterPacketRoutersFor(removedBehaviour, serverEntityPacketRouters, serverEntityPacketArrivalMethods); + UnregisterPacketRoutersFor(removedBehaviour, serverPacketBroadcastEvents, serverBroadcastPacketListenerMethods); + UnregisterPacketRoutersFor(removedBehaviour, serverEntityPacketRouterEvents, serverEntityPacketListenerMethods); } public void ExitUniverse(IUniverse universe) => _networkEntityCollector.Unassign(); + public void EnterUniverse(IUniverse universe) { _networkEntityCollector.Assign(universe); NetworkCommunicator = BehaviourController.GetRequiredBehaviourInParent(); } + #endregion #region Initialization + public NetworkManager() { - CachePacketRetrievalDelegates(typeof(INetworkPacket), broadcastPacketRetrievalDelegates); - CachePacketRetrievalDelegates(typeof(IEntityNetworkPacket), entityPacketRetrievalDelegates); + CacheRetrievalSubscriptionDelegates(); CacheRegistrationMethods(); - CachePacketArrivalMethods(); + CachePacketListenerMethods(); _networkEntityCollector.OnCollected.AddListener(OnCollected); _networkEntityCollector.OnRemoved.AddListener(OnRemoved); } - private void CachePacketRetrievalDelegates(Type packetType, List<(Type packetType, Delegate @delegate)> retrievalDelegates) + /// + /// Caches all retrieval subscription delegates for packets. + /// + private void CacheRetrievalSubscriptionDelegates() { - IEnumerable packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) - .Where(t => packetType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType); + CachePacketRetrievalDelegates(typeof(INetworkPacket), broadcastPacketRetrievalSubscriptionDelegates); + CachePacketRetrievalDelegates(typeof(IEntityNetworkPacket), entityPacketRetrievalSubscriptionDelegates); - MethodInfo onPacketArrivedMethod = GetType() - .GetMethod(nameof(OnPacketReceived), BindingFlags.NonPublic | BindingFlags.Instance)!; + uniqueRetrievalSubscriptionDelegates.AddRange(broadcastPacketRetrievalSubscriptionDelegates.Concat(entityPacketRetrievalSubscriptionDelegates).DistinctBy(pair => pair.PacketType)); + } - foreach (Type pType in packetTypes) + /// + /// Creates delegates for all concrete packet types that forward packets to . + /// + private void CachePacketRetrievalDelegates(Type packetType, List retrievalDelegates) + { + IEnumerable packetTypes = + AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes()) + .Where(t => + packetType.IsAssignableFrom(t) && + !t.IsInterface && + !t.IsAbstract && + !t.IsGenericType + ); + + MethodInfo onPacketArrivedMethod = GetType().GetMethod(nameof(OnPacketReceived), BindingFlags.NonPublic | BindingFlags.Instance)!; + + foreach (Type type in packetTypes) { - MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(pType); + MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(type); + + Type genericDelegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(IConnection), type); - Type genericDelegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(IConnection), pType); Delegate genericPacketReceivedDelegate = Delegate.CreateDelegate(genericDelegateType, this, genericOnPacketArrivedMethod); - retrievalDelegates.Add((pType, genericPacketReceivedDelegate)); + + retrievalDelegates.Add((type, genericPacketReceivedDelegate)); } } + /// + /// Caches all registration and cleanup methods for packets. + /// private void CacheRegistrationMethods() { - CacheRegistrationMethods(registerBroadcastPacketListenersMethods, nameof(RegisterPacketListenerEvent), broadcastPacketRetrievalDelegates); - CacheRegistrationMethods(registerEntityPacketListenersMethods, nameof(RegisterEntityPacketListenerEvent), entityPacketRetrievalDelegates); - CacheRegistrationMethods(clearRoutesMethods, nameof(ClearRouter), broadcastPacketRetrievalDelegates); + CacheRegistrationMethods(registerBroadcastPacketListenersMethods, nameof(RegisterBroadcastPacketListenerEvent), broadcastPacketRetrievalSubscriptionDelegates); + CacheRegistrationMethods(registerEntityPacketListenersMethods, nameof(RegisterEntityPacketListenerEvent), entityPacketRetrievalSubscriptionDelegates); + CacheRegistrationMethods(clearRoutesMethods, nameof(ClearRouter), uniqueRetrievalSubscriptionDelegates); } - private void CacheRegistrationMethods(Dictionary registrationMethods, string methodName, List<(Type packetType, Delegate @delegate)> packetRetrievalDelegates) + /// + /// Creates generic method instances for each packet type listener to be registered into the . + /// + private void CacheRegistrationMethods( + Dictionary listenerRegistrationMethods, + string methodName, + List packetRetrievalDelegates) { MethodInfo registerPacketMethod = typeof(NetworkManager).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static)!; + foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates) { MethodInfo genericMethod = registerPacketMethod.MakeGenericMethod(packetType); - registrationMethods.TryAdd(packetType, genericMethod); + listenerRegistrationMethods.TryAdd(packetType, genericMethod); } } - private void CachePacketArrivalMethods() + /// + /// Caches packet listener methods for all packet listener interfaces. + /// + private void CachePacketListenerMethods() { - CachePacketArrivalMethods(clientBroadcastPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient<>.OnClientPacketArrived)); - CachePacketArrivalMethods(serverBroadcastPacketArrivalMethods, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer<>.OnServerPacketArrived)); - CachePacketArrivalMethods(clientEntityPacketArrivalMethods, typeof(IPacketListenerClientEntity<>), nameof(IPacketListenerClientEntity<>.OnEntityClientPacketArrived)); - CachePacketArrivalMethods(serverEntityPacketArrivalMethods, typeof(IPacketListenerServerEntity<>), nameof(IPacketListenerServerEntity<>.OnEntityServerPacketArrived)); + CachePacketListenerMethods(clientBroadcastPacketListenerMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient<>.OnClientPacketArrived)); + CachePacketListenerMethods(serverBroadcastPacketListenerMethods, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer<>.OnServerPacketArrived)); + CachePacketListenerMethods(clientEntityPacketListenerMethods, typeof(IPacketListenerClientEntity<>), nameof(IPacketListenerClientEntity<>.OnEntityClientPacketArrived)); + CachePacketListenerMethods(serverEntityPacketListenerMethods, typeof(IPacketListenerServerEntity<>), nameof(IPacketListenerServerEntity<>.OnEntityServerPacketArrived)); } - private static void CachePacketArrivalMethods(Dictionary>> packetArrivalMethods, Type listenerType, string packetArrivalMethodName) + /// + /// Discovers all types implementing a given packet listener interface and caches their methods. + /// + private static void CachePacketListenerMethods( + Dictionary>> packetListenerMethods, + Type listenerType, + string packetListenerMethodName) { foreach (Type listenerClass in GetGenericsWith(listenerType)) { - Dictionary> packetRouters = []; - packetArrivalMethods.Add(listenerClass, packetRouters); + Dictionary> listenerMethodDictionary = []; + packetListenerMethods.Add(listenerClass, listenerMethodDictionary); foreach (Type packetListener in GetGenericInterfacesWith(listenerType, listenerClass)) { Type packetType = packetListener.GetGenericArguments().First(); - List arrivalMethods = packetListener - .GetMethods() - .Where(m => m.Name == packetArrivalMethodName) - .ToList(); + List listenerMethods = [.. packetListener.GetMethods().Where(m => m.Name == packetListenerMethodName)]; - packetRouters.Add(packetType, arrivalMethods); + listenerMethodDictionary.Add(packetType, listenerMethods); } } } + /// + /// Finds all types that implement a generic interface. + /// private static IEnumerable GetGenericsWith(Type type) => AppDomain.CurrentDomain .GetAssemblies() .SelectMany(a => a.GetTypes().Where( t => t.GetInterfaces().Any( - i => i.IsGenericType && i.GetGenericTypeDefinition() == type - ) - ) - ); + i => i.IsGenericType && i.GetGenericTypeDefinition() == type))); + // Gets all generic interfaces of a specific definition on a type private static IEnumerable GetGenericInterfacesWith(Type interfaceType, Type type) - => type.GetInterfaces().Where( - i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType - ); + => type.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType); + #endregion + // Identifies whether packet routing is client-side or server-side private enum NetworkType { Client, Server } + + private readonly record struct PacketRetrievalDelegatePair(Type PacketType, Delegate Delegate) + { + public static implicit operator (Type packetType, Delegate @delegate)(PacketRetrievalDelegatePair value) => (value.PacketType, value.Delegate); + public static implicit operator PacketRetrievalDelegatePair((Type packetType, Delegate @delegate) value) => new(value.packetType, value.@delegate); + } }