using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Syntriax.Engine.Core; namespace Syntriax.Engine.Network; public class NetworkManagerSlo : UniverseObject, INetworkManager { private readonly List<(Type packetType, Delegate callback)> packetDelegates = []; private readonly Dictionary>> clientListenerDelegates = []; private readonly Dictionary>> serverListenerDelegates = []; private readonly Dictionary>> clientPacketListeners = []; private readonly Dictionary>> serverPacketListeners = []; private readonly Dictionary _networkEntities = []; private readonly BehaviourCollector _networkEntityCollector = new(); public IReadOnlyDictionary NetworkEntities => _networkEntities; public IBehaviourCollector NetworkEntityCollector => _networkEntityCollector; private INetworkCommunicator _networkCommunicator = null!; public INetworkCommunicator NetworkCommunicator { get => _networkCommunicator; set { if (_networkCommunicator == value) return; INetworkCommunicator previousCommunicator = _networkCommunicator; _networkCommunicator = value; if (previousCommunicator is not null) UnsubscribeDelegates(previousCommunicator); if (_networkCommunicator is not null) SubscribeDelegates(_networkCommunicator); } } #region Network Communicator Subscriptions private static MethodInfo? GetCommunicatorMethod(string methodName) => typeof(INetworkCommunicator).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); private void SubscribeDelegates(INetworkCommunicator communicator) { MethodInfo subscribeMethod = GetCommunicatorMethod(nameof(INetworkCommunicator.SubscribeToPackets))!; foreach ((Type packetType, Delegate callback) in packetDelegates) { MethodInfo genericMethod = subscribeMethod.MakeGenericMethod(packetType); genericMethod.Invoke(communicator, [callback]); } } private void UnsubscribeDelegates(INetworkCommunicator communicator) { MethodInfo unsubscribeMethod = GetCommunicatorMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets))!; foreach ((Type packetType, Delegate callback) in packetDelegates) { MethodInfo genericMethod = unsubscribeMethod.MakeGenericMethod(packetType); genericMethod.Invoke(communicator, [callback]); } } #endregion #region Engine Callbacks private void OnEntityCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args) { INetworkEntity entity = args.BehaviourCollected; if (!_networkEntities.TryAdd(entity.Id, entity)) throw new Exception($"Unable to add {entity.Id} to {nameof(NetworkManager)}"); RegisterEntityListeners(entity); } private void OnEntityRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args) { INetworkEntity removedBehaviour = args.BehaviourRemoved; if (!_networkEntities.Remove(args.BehaviourRemoved.Id)) return; UnRegisterEntityListeners(removedBehaviour); } protected override void OnExitingUniverse(IUniverse universe) { _networkEntityCollector.Unassign(); } protected override void OnEnteringUniverse(IUniverse universe) { _networkEntityCollector.Assign(universe); NetworkCommunicator = this.GetRequiredUniverseObjectInParent(); } #endregion #region Packet Retrieval private void OnPacketReceived(string senderClientId, T packet) { if (packet is IEntityNetworkPacket entityPacket) RouteEntityPacket(senderClientId, entityPacket.EntityId, packet); else RouteBroadcastPacket(senderClientId, packet!); } private void RouteEntityPacket(string senderClientId, string entityId, T packet) { if (NetworkCommunicator is INetworkCommunicatorClient) InvokeListenerIfExists(clientPacketListeners, entityId, senderClientId, packet!); if (NetworkCommunicator is INetworkCommunicatorServer) InvokeListenerIfExists(serverPacketListeners, entityId, senderClientId, packet!); } private void RouteBroadcastPacket(string senderClientId, T packet) { if (NetworkCommunicator is INetworkCommunicatorClient) InvokeAllListeners(clientPacketListeners, senderClientId, packet!); if (NetworkCommunicator is INetworkCommunicatorServer) InvokeAllListeners(serverPacketListeners, senderClientId, packet!); } private static void InvokeListenerIfExists( Dictionary>> listeners, string entityId, string senderClientId, T packet) { if (!listeners.TryGetValue(packet!.GetType(), out Dictionary>? entityListeners)) return; if (entityListeners.TryGetValue(entityId, out Event? listenerEvent)) listenerEvent.Invoke(senderClientId, packet); } private static void InvokeAllListeners( Dictionary>> listeners, string senderClientId, T packet) { if (!listeners.TryGetValue(packet!.GetType(), out Dictionary>? entityListeners)) return; foreach ((string _, Event listenerEvent) in entityListeners) listenerEvent.Invoke(senderClientId, packet); } #endregion #region Listener Setups private void RegisterEntityListeners(INetworkEntity entity) { RegisterEntityListenersForSide(entity, clientListenerDelegates, clientPacketListeners, isServer: false); RegisterEntityListenersForSide(entity, serverListenerDelegates, serverPacketListeners, isServer: true); } private static void RegisterEntityListenersForSide( INetworkEntity entity, Dictionary>> listenerDelegates, Dictionary>> packetListeners, bool isServer) { if (!listenerDelegates.TryGetValue(entity.GetType(), out Dictionary>? interfaceDelegates)) return; foreach ((Type interfaceType, List delegateDataList) in interfaceDelegates) foreach ((Type parameterType, MethodInfo receiveMethod) in delegateDataList) { Dictionary> listeners = GetOrCreateListenersForPacketType(packetListeners, parameterType); Event listenerEvent = CreateListenerEvent(entity, receiveMethod, isServer); listeners.Add(entity.Id, listenerEvent); } } private void UnRegisterEntityListeners(INetworkEntity entity) { UnregisterEntityListenersForSide(entity, clientListenerDelegates, clientPacketListeners); UnregisterEntityListenersForSide(entity, serverListenerDelegates, serverPacketListeners); } private static void UnregisterEntityListenersForSide( INetworkEntity entity, Dictionary>> listenerDelegates, Dictionary>> packetListeners) { if (!listenerDelegates.TryGetValue(entity.GetType(), out Dictionary>? interfaceDelegates)) return; foreach ((Type interfaceType, List delegateDataList) in interfaceDelegates) foreach ((Type parameterType, MethodInfo receiveMethod) in delegateDataList) { Dictionary> listeners = GetOrCreateListenersForPacketType(packetListeners, parameterType); if (!listeners.TryGetValue(entity.Id, out Event? listenerEvent)) continue; listeners.Remove(entity.Id); listenerDelegates.Clear(); } } private static Dictionary> GetOrCreateListenersForPacketType( Dictionary>> packetListeners, Type packetType) { if (!packetListeners.TryGetValue(packetType, out Dictionary>? listeners)) { listeners = []; packetListeners.Add(packetType, listeners); } return listeners; } private static Event CreateListenerEvent(INetworkEntity entity, MethodInfo receiveMethod, bool isServer) { Event listenerEvent = new(); if (isServer) listenerEvent.AddListener((sender, packet) => receiveMethod.Invoke(entity, [sender, packet])); else listenerEvent.AddListener((sender, packet) => receiveMethod.Invoke(entity, [packet])); return listenerEvent; } #endregion #region Initialization private static IEnumerable DiscoverPacketTypes() => AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where( t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType ); private static IEnumerable DiscoverClassesImplementing(Type interfaceType) => AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where( t => t.GetInterfaces().Any( i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType ) ); private static IEnumerable GetInterfacesOfType(Type type, Type interfaceType) => type.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType); private static void CacheListenerDelegatesForSide(Dictionary>> targetCache, Type listenerInterfaceType, string methodName) { IEnumerable listenerClasses = DiscoverClassesImplementing(listenerInterfaceType); foreach (Type listenerClass in listenerClasses) { Dictionary> interfaceListeners = []; targetCache.Add(listenerClass, interfaceListeners); IEnumerable interfaces = GetInterfacesOfType(listenerClass, listenerInterfaceType); foreach (Type listenerInterface in interfaces) { Type packetType = listenerInterface.GetGenericArguments().First(); List methods = listenerInterface.GetMethods() .Where(m => m.Name == methodName) .Select(m => new DelegateData(packetType, m)) .ToList(); interfaceListeners.Add(packetType, methods); } } } private void CacheListenerDelegates() { CacheListenerDelegatesForSide(clientListenerDelegates, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient.OnClientPacketArrived)); CacheListenerDelegatesForSide(serverListenerDelegates, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer.OnServerPacketArrived)); } private void CachePacketDelegates() { IEnumerable packetTypes = DiscoverPacketTypes(); MethodInfo onPacketReceivedMethod = GetType().GetMethod( nameof(OnPacketReceived), BindingFlags.NonPublic | BindingFlags.Instance )!; foreach (Type packetType in packetTypes) { MethodInfo genericMethod = onPacketReceivedMethod.MakeGenericMethod(packetType); Type delegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(string), packetType); Delegate packetDelegate = Delegate.CreateDelegate(delegateType, this, genericMethod); packetDelegates.Add((packetType, packetDelegate)); } } public NetworkManagerSlo() { CachePacketDelegates(); CacheListenerDelegates(); _networkEntityCollector.OnCollected.AddListener(OnEntityCollected); _networkEntityCollector.OnRemoved.AddListener(OnEntityRemoved); } #endregion private readonly 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); } }