diff --git a/Shared/Network/NetworkManager.cs b/Shared/Network/NetworkManager.cs
index b86c6fd..1294e23 100644
--- a/Shared/Network/NetworkManager.cs
+++ b/Shared/Network/NetworkManager.cs
@@ -7,32 +7,18 @@ using Syntriax.Engine.Core;
namespace Syntriax.Engine.Network;
+///
+/// Intermediary manager that looks up in it's hierarchy for a to route/broadcast it's received packets to their destinations.
+///
public class NetworkManager : UniverseObject, INetworkManager
{
- private INetworkCommunicator networkCommunicator = null!;
+ private readonly Dictionary>> clientPacketArrivalMethods = [];
+ private readonly Dictionary>> serverPacketArrivalMethods = [];
- private readonly Dictionary>> clientListenerDelegates = [];
- private readonly Dictionary>> serverListenerDelegates = [];
+ private readonly Dictionary>> clientPacketRouters = [];
+ private readonly Dictionary>> serverPacketRouters = [];
- private readonly Dictionary>> clientPacketListeners = [];
- private readonly Dictionary>> serverPacketListeners = [];
- private readonly List<(Type packetType, Delegate callback)> delegates = [];
-
- public INetworkCommunicator NetworkCommunicator
- {
- get => networkCommunicator;
- set
- {
- if (networkCommunicator == value)
- return;
-
- var previousCommunicator = networkCommunicator;
- networkCommunicator = value;
-
- if (previousCommunicator is not null) UnsubscribeDelegates(networkCommunicator);
- if (networkCommunicator is not null) SubscribeDelegates(networkCommunicator);
- }
- }
+ private readonly List<(Type packetType, Delegate callback)> packetRetrievalDelegates = [];
private readonly Dictionary _networkEntities = [];
public IReadOnlyDictionary NetworkEntities => _networkEntities;
@@ -40,45 +26,205 @@ public class NetworkManager : UniverseObject, INetworkManager
private readonly BehaviourCollector _networkEntityCollector = new();
public IBehaviourCollector NetworkEntityCollector => _networkEntityCollector;
+ private INetworkCommunicator _networkCommunicator = null!;
+ public INetworkCommunicator NetworkCommunicator
+ {
+ get => _networkCommunicator;
+ set
+ {
+ if (_networkCommunicator == value)
+ return;
+
+ var previousCommunicator = _networkCommunicator;
+ _networkCommunicator = value;
+
+ if (previousCommunicator is not null) SubscribeCommunicatorMethods(previousCommunicator);
+ if (_networkCommunicator is not null) UnsubscribeCommunicatorMethods(_networkCommunicator);
+ }
+ }
+
+ #region Communicator Subscriptions
+ private void SubscribeCommunicatorMethods(INetworkCommunicator networkCommunicator)
+ {
+ MethodInfo subscribeToPacketsMethod = typeof(INetworkCommunicator)
+ .GetMethod(nameof(INetworkCommunicator.SubscribeToPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!;
+
+ foreach ((Type packetType, Delegate callback) in packetRetrievalDelegates)
+ {
+ MethodInfo genericSubscribeMethod = subscribeToPacketsMethod.MakeGenericMethod(packetType);
+ genericSubscribeMethod.Invoke(networkCommunicator, [callback]);
+ }
+ }
+
+ private void UnsubscribeCommunicatorMethods(INetworkCommunicator networkCommunicator)
+ {
+ MethodInfo unsubscribeFromPacketsMethod = typeof(INetworkCommunicator)
+ .GetMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!;
+
+ foreach ((Type packetType, Delegate callback) in packetRetrievalDelegates)
+ {
+ MethodInfo genericUnsubscribeMethod = unsubscribeFromPacketsMethod.MakeGenericMethod(packetType);
+ genericUnsubscribeMethod.Invoke(networkCommunicator, [callback]);
+ }
+ }
+ #endregion
+
+ #region Packet Routing
+ private void OnPacketReceived(string senderClientId, T entityDataPacket)
+ {
+ if (entityDataPacket is IEntityNetworkPacket entityPacket)
+ RoutePacket(senderClientId, entityDataPacket, entityPacket);
+ else
+ BroadcastPacket(senderClientId, entityDataPacket);
+ }
+
+ private void RoutePacket(string senderClientId, T entityDataPacket, IEntityNetworkPacket entityPacket)
+ {
+ if (NetworkCommunicator is INetworkCommunicatorClient)
+ RoutePacket(clientPacketRouters, entityPacket.EntityId, senderClientId, entityDataPacket);
+ if (NetworkCommunicator is INetworkCommunicatorServer)
+ RoutePacket(serverPacketRouters, entityPacket.EntityId, senderClientId, entityDataPacket);
+ }
+
+ private void BroadcastPacket(string senderClientId, T entityDataPacket)
+ {
+ if (NetworkCommunicator is INetworkCommunicatorClient)
+ BroadcastPacket(clientPacketRouters, senderClientId, entityDataPacket);
+ if (NetworkCommunicator is INetworkCommunicatorServer)
+ BroadcastPacket(serverPacketRouters, senderClientId, entityDataPacket);
+ }
+
+
+ private static void BroadcastPacket(
+ Dictionary>> packetRouters,
+ string senderClientId,
+ T entityDataPacket)
+ {
+ if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary>? routers))
+ return;
+
+ foreach ((string id, Event routerEvent) in routers)
+ routerEvent.Invoke(senderClientId, entityDataPacket!);
+ }
+
+ private static void RoutePacket(
+ Dictionary>> packetRouters,
+ string entityId,
+ string senderClientId,
+ T entityDataPacket)
+ {
+ if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary>? routers))
+ return;
+
+ if (!routers.TryGetValue(entityId, out Event? routerEvent))
+ return;
+
+ routerEvent.Invoke(senderClientId, entityDataPacket!);
+ }
+ #endregion
+
+ #region Packet Routers
+ private static void RegisterPacketRoutersFor(
+ INetworkEntity behaviour,
+ Dictionary>> packetRouters,
+ Dictionary>> packetArrivalMethods,
+ NetworkType networkType)
+ {
+ if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary>? arrivalMethods))
+ return;
+
+ foreach ((Type packetType, List methods) in arrivalMethods)
+ foreach (MethodInfo receiveMethod in methods)
+ {
+ if (!packetRouters.TryGetValue(packetType, out Dictionary>? routers))
+ {
+ routers = [];
+ packetRouters.Add(packetType, routers);
+ }
+
+ Event packetListenerEvent = new();
+ RegisterPacketListenerEvent(behaviour, packetListenerEvent, receiveMethod, networkType);
+ routers.Add(behaviour.Id, packetListenerEvent);
+ }
+ }
+
+ private static void RegisterPacketListenerEvent(
+ INetworkEntity behaviour,
+ Event packetListenerEvent,
+ MethodInfo receiveMethod,
+ NetworkType networkType)
+ {
+ switch (networkType)
+ {
+ case NetworkType.Client: packetListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(behaviour, [@object])); break;
+ case NetworkType.Server: packetListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(behaviour, [sender, @object])); break;
+ }
+ }
+
+ private static void UnregisterPacketRoutersFor(
+ INetworkEntity behaviour,
+ Dictionary>> packetRouters,
+ Dictionary>> packetArrivalMethods)
+ {
+ if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary>? arrivalMethods))
+ return;
+
+ foreach ((Type packetType, List methods) in arrivalMethods)
+ {
+ if (!packetRouters.TryGetValue(packetType, out Dictionary>? routers))
+ continue;
+
+ if (!routers.TryGetValue(behaviour.Id, out Event? routerEvent))
+ continue;
+
+ routers.Remove(behaviour.Id);
+ routerEvent.Clear();
+ }
+ }
+ #endregion
+
+ #region Engine Callbacks
+ 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, clientPacketRouters, clientPacketArrivalMethods, NetworkType.Client);
+ RegisterPacketRoutersFor(collectedBehaviour, serverPacketRouters, serverPacketArrivalMethods, NetworkType.Server);
+ }
+
+ private void OnRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args)
+ {
+ INetworkEntity removedBehaviour = args.BehaviourRemoved;
+ if (!_networkEntities.Remove(args.BehaviourRemoved.Id))
+ return;
+
+ UnregisterPacketRoutersFor(removedBehaviour, clientPacketRouters, clientPacketArrivalMethods);
+ UnregisterPacketRoutersFor(removedBehaviour, serverPacketRouters, serverPacketArrivalMethods);
+ }
+
+ protected override void OnExitingUniverse(IUniverse universe) => _networkEntityCollector.Unassign();
+ protected override void OnEnteringUniverse(IUniverse universe)
+ {
+ _networkEntityCollector.Assign(universe);
+ NetworkCommunicator = this.GetRequiredUniverseObjectInParent();
+ }
+ #endregion
+
+ #region Initialization
public NetworkManager()
{
- CachePacketDelegates();
- CacheListenerDelegates();
+ CachePacketRetrievalDelegates();
+ CachePacketArrivalMethods();
_networkEntityCollector.OnCollected.AddListener(OnCollected);
_networkEntityCollector.OnRemoved.AddListener(OnRemoved);
}
- private void CacheListenerDelegates()
+ private void CachePacketRetrievalDelegates()
{
- foreach (Type clientListenerClass in GetClassesImplementing(typeof(IPacketListenerClient<>)))
- {
- Dictionary> clientInterfaceListeners = [];
- clientListenerDelegates.Add(clientListenerClass, clientInterfaceListeners);
- foreach (Type clientListenerInterface in GetInterfacesImplementing(clientListenerClass, typeof(IPacketListenerClient<>)))
- {
- Type clientListenerParameterType = clientListenerInterface.GetGenericArguments().First();
- List delegateDataList = clientListenerInterface.GetMethods().Where(m => m.Name == nameof(IPacketListenerClient.OnClientPacketArrived)).Select(m => new DelegateData(clientListenerParameterType, m)).ToList();
- clientInterfaceListeners.Add(clientListenerParameterType, delegateDataList);
- }
- }
-
- foreach (Type serverListenerClass in GetClassesImplementing(typeof(IPacketListenerServer<>)))
- {
- Dictionary> serverInterfaceListeners = [];
- serverListenerDelegates.Add(serverListenerClass, serverInterfaceListeners);
- foreach (Type serverListenerInterface in GetInterfacesImplementing(serverListenerClass, typeof(IPacketListenerServer<>)))
- {
- Type serverListenerParameterType = serverListenerInterface.GetGenericArguments().First();
- List delegateDataList = serverListenerInterface.GetMethods().Where(m => m.Name == nameof(IPacketListenerServer.OnServerPacketArrived)).Select(m => new DelegateData(serverListenerParameterType, m)).ToList();
- serverInterfaceListeners.Add(serverListenerParameterType, delegateDataList);
- }
- }
- }
-
- private void CachePacketDelegates()
- {
- // 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);
@@ -92,37 +238,38 @@ public class NetworkManager : UniverseObject, INetworkManager
Type genericDelegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(string), packetType);
Delegate genericPacketReceivedDelegate = Delegate.CreateDelegate(genericDelegateType, this, genericOnPacketArrivedMethod);
- delegates.Add((packetType, genericPacketReceivedDelegate));
+ packetRetrievalDelegates.Add((packetType, genericPacketReceivedDelegate));
}
}
- private void SubscribeDelegates(INetworkCommunicator networkCommunicator)
+ private void CachePacketArrivalMethods()
{
- MethodInfo subscribeToPacketsMethod = typeof(INetworkCommunicator)
- .GetMethod(nameof(INetworkCommunicator.SubscribeToPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!;
-
- foreach ((Type packetType, Delegate callback) in delegates)
- {
- MethodInfo genericSubscribeMethod = subscribeToPacketsMethod.MakeGenericMethod(packetType);
-
- genericSubscribeMethod.Invoke(networkCommunicator, [callback]);
- }
+ CachePacketArrivalMethods(clientPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient.OnClientPacketArrived));
+ CachePacketArrivalMethods(serverPacketArrivalMethods, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer.OnServerPacketArrived));
}
- private void UnsubscribeDelegates(INetworkCommunicator networkCommunicator)
+ private static void CachePacketArrivalMethods(Dictionary>> packetArrivalMethods, Type listenerType, string packetArrivalMethodName)
{
- MethodInfo unsubscribeFromPacketsMethod = typeof(INetworkCommunicator)
- .GetMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!;
-
- foreach ((Type packetType, Delegate callback) in delegates)
+ foreach (Type listenerClass in GetGenericsWith(listenerType))
{
- MethodInfo genericUnsubscribeMethod = unsubscribeFromPacketsMethod.MakeGenericMethod(packetType);
+ Dictionary> packetRouters = [];
+ packetArrivalMethods.Add(listenerClass, packetRouters);
- genericUnsubscribeMethod.Invoke(networkCommunicator, [callback]);
+ foreach (Type packetListener in GetGenericInterfacesWith(listenerType, listenerClass))
+ {
+ Type packetType = packetListener.GetGenericArguments().First();
+
+ List arrivalMethods = packetListener
+ .GetMethods()
+ .Where(m => m.Name == packetArrivalMethodName)
+ .ToList();
+
+ packetRouters.Add(packetType, arrivalMethods);
+ }
}
}
- private static IEnumerable GetClassesImplementing(Type type)
+ private static IEnumerable GetGenericsWith(Type type)
=> AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a =>
@@ -133,124 +280,11 @@ public class NetworkManager : UniverseObject, INetworkManager
)
);
- private static IEnumerable GetInterfacesImplementing(Type type, Type interfaceType)
+ private static IEnumerable GetGenericInterfacesWith(Type interfaceType, Type type)
=> type.GetInterfaces().Where(
i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType
);
+ #endregion
- private void OnPacketReceived(string senderClientId, T entityDataPacket)
- {
- Type packetType = typeof(T);
-
- if (entityDataPacket is IEntityNetworkPacket entityPacket)
- {
- if (networkCommunicator is INetworkCommunicatorClient)
- if (clientPacketListeners.TryGetValue(packetType, out Dictionary>? clientListeners))
- if (clientListeners.TryGetValue(entityPacket.EntityId, out Event? clientListenerData))
- clientListenerData.Invoke(senderClientId, entityDataPacket!);
-
- if (networkCommunicator is INetworkCommunicatorServer)
- if (serverPacketListeners.TryGetValue(packetType, out Dictionary>? serverListeners))
- if (serverListeners.TryGetValue(entityPacket.EntityId, out Event? serverListenerData))
- serverListenerData.Invoke(senderClientId, entityDataPacket!);
-
- return;
- }
-
- if (networkCommunicator is INetworkCommunicatorClient)
- if (clientPacketListeners.TryGetValue(packetType, out Dictionary>? clientListeners))
- foreach ((string id, Event clientListenerData) in clientListeners)
- clientListenerData.Invoke(senderClientId, entityDataPacket!);
-
- if (networkCommunicator is INetworkCommunicatorServer)
- if (serverPacketListeners.TryGetValue(packetType, out Dictionary>? serverListeners))
- foreach ((string id, Event serverListenerData) in serverListeners)
- serverListenerData.Invoke(senderClientId, entityDataPacket!);
- }
-
- 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)}");
-
- if (clientListenerDelegates.TryGetValue(collectedBehaviour.GetType(), out Dictionary>? clientInterfaceDelegates))
- foreach ((Type clientListenerInterfaceType, List clientDelegateDataList) in clientInterfaceDelegates)
- foreach ((Type parameterType, MethodInfo receiveMethod) in clientDelegateDataList)
- {
- if (!clientPacketListeners.TryGetValue(parameterType, out Dictionary>? clientListeners))
- {
- clientListeners = [];
- clientPacketListeners.Add(parameterType, clientListeners);
- }
-
- Event clientListenerEvent = new();
- clientListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(collectedBehaviour, [@object]));
- clientListeners.Add(collectedBehaviour.Id, clientListenerEvent);
- }
-
- if (serverListenerDelegates.TryGetValue(collectedBehaviour.GetType(), out Dictionary>? serverInterfaceDelegates))
- foreach ((Type serverListenerInterfaceType, List serverDelegateDataList) in serverInterfaceDelegates)
- foreach ((Type parameterType, MethodInfo receiveMethod) in serverDelegateDataList)
- {
- if (!serverPacketListeners.TryGetValue(parameterType, out Dictionary>? serverListeners))
- {
- serverListeners = [];
- serverPacketListeners.Add(parameterType, serverListeners);
- }
-
- Event serverListenerEvent = new();
- serverListenerEvent.AddListener((sender, @object) => receiveMethod.Invoke(collectedBehaviour, [sender, @object]));
- serverListeners.Add(collectedBehaviour.Id, serverListenerEvent);
- }
- }
-
- private void OnRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args)
- {
- INetworkEntity removedBehaviour = args.BehaviourRemoved;
- if (!_networkEntities.Remove(args.BehaviourRemoved.Id))
- return;
-
- if (clientListenerDelegates.TryGetValue(removedBehaviour.GetType(), out Dictionary>? clientInterfaceDelegates))
- foreach ((Type clientListenerInterfaceType, List clientDelegateDataList) in clientInterfaceDelegates)
- foreach ((Type parameterType, MethodInfo receiveMethod) in clientDelegateDataList)
- {
- if (!clientPacketListeners.TryGetValue(parameterType, out Dictionary>? clientListeners))
- continue;
-
- if (!clientListeners.TryGetValue(removedBehaviour.Id, out Event? clientListenerEvent))
- continue;
-
- clientListeners.Remove(removedBehaviour.Id);
- clientListenerEvent.Clear();
- }
-
- if (serverListenerDelegates.TryGetValue(removedBehaviour.GetType(), out Dictionary>? serverInterfaceDelegates))
- foreach ((Type serverListenerInterfaceType, List serverDelegateDataList) in serverInterfaceDelegates)
- foreach ((Type parameterType, MethodInfo receiveMethod) in serverDelegateDataList)
- {
- if (!serverPacketListeners.TryGetValue(parameterType, out Dictionary>? serverListeners))
- continue;
-
- if (!serverListeners.TryGetValue(removedBehaviour.Id, out Event? serverListenerEvent))
- continue;
-
- serverListeners.Remove(removedBehaviour.Id);
- serverListenerEvent.Clear();
- }
- }
-
- protected override void OnExitingUniverse(IUniverse universe) => _networkEntityCollector.Unassign();
- protected override void OnEnteringUniverse(IUniverse universe)
- {
- _networkEntityCollector.Assign(universe);
- NetworkCommunicator = this.GetRequiredUniverseObjectInParent();
- }
-
- private record struct DelegateData(Type ParameterType, MethodInfo ReceiveMethod)
- {
- public static implicit operator (Type ParameterType, MethodInfo ReceiveMethod)(DelegateData value) => (value.ParameterType, value.ReceiveMethod);
- public static implicit operator DelegateData((Type ParameterType, MethodInfo ReceiveMethod) value) => new(value.ParameterType, value.ReceiveMethod);
- }
+ private enum NetworkType { Client, Server }
}