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 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> clientPacketListeners = []; private readonly Dictionary> serverPacketListeners = []; private readonly Dictionary _networkEntities = []; public IReadOnlyDictionary NetworkEntities => _networkEntities; private readonly BehaviourCollector _networkEntityCollector = new(); public IBehaviourCollector NetworkEntityCollector => _networkEntityCollector; public NetworkManager() { CacheDelegates(); _networkEntityCollector.OnCollected += OnCollected; _networkEntityCollector.OnRemoved += OnRemoved; } private void CacheDelegates() { // Find network packets implementing EntityDataPacket<> IEnumerable packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) .Where(t => typeof(IEntityNetworkPacket).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(Action<,>).MakeGenericType(packetType, typeof(string)); 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 void OnPacketReceived(T entityDataPacket, string fromClientId) { Type packetType = typeof(T); if (networkCommunicator is INetworkCommunicatorClient) if (clientPacketListeners.TryGetValue(packetType, out List? clientListeners)) foreach (object clientListener in clientListeners) { MethodInfo clientListenerReceiveMethod = typeof(IPacketListenerClient<>).MakeGenericType(packetType).GetMethods().First(m => m.Name == nameof(IPacketListenerClient.OnClientPacketArrived)); clientListenerReceiveMethod.Invoke(clientListener, [entityDataPacket]); } if (networkCommunicator is INetworkCommunicatorServer) if (serverPacketListeners.TryGetValue(packetType, out List? serverListeners)) foreach (object serverListener in serverListeners) { MethodInfo serverListenerReceiveMethod = typeof(IPacketListenerServer<>).MakeGenericType(packetType).GetMethods().First(m => m.Name == nameof(IPacketListenerServer.OnServerPacketArrived)); serverListenerReceiveMethod.Invoke(serverListener, [entityDataPacket, fromClientId]); } } private void OnCollected(IBehaviourCollector sender, INetworkEntity behaviourCollected) { if (!_networkEntities.TryAdd(behaviourCollected.Id, behaviourCollected)) throw new($"Unable to add {behaviourCollected.Id} to {nameof(NetworkManager)}"); foreach (Type clientListenerType in behaviourCollected.GetType().GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketListenerClient<>))) { Type clientListenerParameterType = clientListenerType.GetGenericArguments().First(); if (!clientPacketListeners.TryGetValue(clientListenerParameterType, out List? clientListeners)) { clientListeners = []; clientPacketListeners.Add(clientListenerParameterType, clientListeners); } clientListeners.Add(behaviourCollected); } foreach (Type serverListenerType in behaviourCollected.GetType().GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketListenerServer<>))) { Type serverListenerParameterType = serverListenerType.GetGenericArguments().First(); if (!serverPacketListeners.TryGetValue(serverListenerParameterType, out List? serverListeners)) { serverListeners = []; serverPacketListeners.Add(serverListenerParameterType, serverListeners); } serverListeners.Add(behaviourCollected); } } private void OnRemoved(IBehaviourCollector sender, INetworkEntity behaviourRemoved) { _networkEntities.Remove(behaviourRemoved.Id); } protected override void OnExitingUniverse(IUniverse universe) => _networkEntityCollector.Unassign(); protected override void OnEnteringUniverse(IUniverse universe) { _networkEntityCollector.Assign(universe); NetworkCommunicator = BehaviourController.GetRequiredBehaviourInParent(); } }