using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Syntriax.Engine.Core; namespace Syntriax.Engine.Network; public class NetworkManager : UniverseObject, INetworkManager { private INetworkCommunicator networkCommunicator = null!; private readonly Dictionary>> clientListenerDelegates = []; private readonly Dictionary>> serverListenerDelegates = []; 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 Dictionary _networkEntities = []; public IReadOnlyDictionary NetworkEntities => _networkEntities; private readonly BehaviourCollector _networkEntityCollector = new(); public IBehaviourCollector NetworkEntityCollector => _networkEntityCollector; public NetworkManager() { CachePacketDelegates(); CacheListenerDelegates(); _networkEntityCollector.OnCollected.AddListener(OnCollected); _networkEntityCollector.OnRemoved.AddListener(OnRemoved); } private void CacheListenerDelegates() { 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); MethodInfo onPacketArrivedMethod = GetType() .GetMethod(nameof(OnPacketReceived), BindingFlags.NonPublic | BindingFlags.Instance)!; foreach (Type packetType in packetTypes) { MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType); Type genericDelegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(string), packetType); Delegate genericPacketReceivedDelegate = Delegate.CreateDelegate(genericDelegateType, this, genericOnPacketArrivedMethod); delegates.Add((packetType, genericPacketReceivedDelegate)); } } private void SubscribeDelegates(INetworkCommunicator networkCommunicator) { 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]); } } private void UnsubscribeDelegates(INetworkCommunicator networkCommunicator) { MethodInfo unsubscribeFromPacketsMethod = typeof(INetworkCommunicator) .GetMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!; foreach ((Type packetType, Delegate callback) in delegates) { MethodInfo genericUnsubscribeMethod = unsubscribeFromPacketsMethod.MakeGenericMethod(packetType); genericUnsubscribeMethod.Invoke(networkCommunicator, [callback]); } } private static IEnumerable GetClassesImplementing(Type type) => AppDomain.CurrentDomain .GetAssemblies() .SelectMany(a => a.GetTypes().Where( t => t.GetInterfaces().Any( i => i.IsGenericType && i.GetGenericTypeDefinition() == type ) ) ); private static IEnumerable GetInterfacesImplementing(Type type, Type interfaceType) => type.GetInterfaces().Where( i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType ); 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) { _networkEntities.Remove(args.BehaviourRemoved.Id); } 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); } }