From 7a499bbcbcab5e829acff99207eeadbdad3405fa Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 5 Aug 2025 19:28:49 +0300 Subject: [PATCH] chore: migrated networking code to engine --- Engine | 2 +- Platforms/Desktop/Program.cs | 2 +- Platforms/Server/PongEndpoints.cs | 2 +- Pong.sln | 15 + Shared/Behaviours/Ball.cs | 2 +- Shared/Behaviours/Paddle.cs | 2 +- Shared/Behaviours/PongGameStarter.cs | 2 +- Shared/Behaviours/PongManager.cs | 2 +- Shared/Network/Abstract/IConnection.cs | 12 - .../Network/Abstract/IEntityNetworkPacket.cs | 6 - .../Network/Abstract/INetworkCommunicator.cs | 37 -- Shared/Network/Abstract/INetworkEntity.cs | 5 - Shared/Network/Abstract/INetworkManager.cs | 11 - Shared/Network/Abstract/INetworkPacket.cs | 3 - .../Network/Abstract/IPacketListenerClient.cs | 6 - .../Network/Abstract/IPacketListenerServer.cs | 6 - Shared/Network/Abstract/PacketDelivery.cs | 9 - Shared/Network/LiteNetLib/LiteNetLibClient.cs | 89 ----- .../LiteNetLib/LiteNetLibClientConnection.cs | 16 - .../LiteNetLib/LiteNetLibCommunicatorBase.cs | 189 ---------- Shared/Network/LiteNetLib/LiteNetLibServer.cs | 104 ------ .../LiteNetLib/LiteNetLibServerConnection.cs | 16 - .../LiteNetLib/Packers/AABBNetPacker.cs | 22 -- .../LiteNetLib/Packers/CircleNetPacker.cs | 22 -- .../LiteNetLib/Packers/ColorHSVNetPacker.cs | 24 -- .../LiteNetLib/Packers/ColorRGBANetPacker.cs | 26 -- .../LiteNetLib/Packers/ColorRGBNetPacker.cs | 24 -- .../Packers/Line2DEquationNetPacker.cs | 22 -- .../LiteNetLib/Packers/Line2DNetPacker.cs | 22 -- .../Packers/Projection1DNetPacker.cs | 22 -- .../LiteNetLib/Packers/QuaternionNetPacker.cs | 26 -- .../LiteNetLib/Packers/Shape2DNetPacker.cs | 28 -- .../LiteNetLib/Packers/TriangleNetPacker.cs | 24 -- .../LiteNetLib/Packers/Vector2DNetPacker.cs | 22 -- .../LiteNetLib/Packers/Vector3DNetPacker.cs | 24 -- Shared/Network/NetworkManager.cs | 333 ------------------ Shared/PongUniverse.cs | 2 +- Shared/Shared.csproj | 2 +- 38 files changed, 24 insertions(+), 1159 deletions(-) delete mode 100644 Shared/Network/Abstract/IConnection.cs delete mode 100644 Shared/Network/Abstract/IEntityNetworkPacket.cs delete mode 100644 Shared/Network/Abstract/INetworkCommunicator.cs delete mode 100644 Shared/Network/Abstract/INetworkEntity.cs delete mode 100644 Shared/Network/Abstract/INetworkManager.cs delete mode 100644 Shared/Network/Abstract/INetworkPacket.cs delete mode 100644 Shared/Network/Abstract/IPacketListenerClient.cs delete mode 100644 Shared/Network/Abstract/IPacketListenerServer.cs delete mode 100644 Shared/Network/Abstract/PacketDelivery.cs delete mode 100644 Shared/Network/LiteNetLib/LiteNetLibClient.cs delete mode 100644 Shared/Network/LiteNetLib/LiteNetLibClientConnection.cs delete mode 100644 Shared/Network/LiteNetLib/LiteNetLibCommunicatorBase.cs delete mode 100644 Shared/Network/LiteNetLib/LiteNetLibServer.cs delete mode 100644 Shared/Network/LiteNetLib/LiteNetLibServerConnection.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/AABBNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/CircleNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/ColorHSVNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/ColorRGBANetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/ColorRGBNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/Line2DEquationNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/Line2DNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/Projection1DNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/QuaternionNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/Shape2DNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/TriangleNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/Vector2DNetPacker.cs delete mode 100644 Shared/Network/LiteNetLib/Packers/Vector3DNetPacker.cs delete mode 100644 Shared/Network/NetworkManager.cs diff --git a/Engine b/Engine index b8217f2..1644a75 160000 --- a/Engine +++ b/Engine @@ -1 +1 @@ -Subproject commit b8217f2106dd056c0a1d9eb591e2513e466b2389 +Subproject commit 1644a751bb81ab0f0d467321d1dd5cd2e326227a diff --git a/Platforms/Desktop/Program.cs b/Platforms/Desktop/Program.cs index 0771b07..b19865d 100644 --- a/Platforms/Desktop/Program.cs +++ b/Platforms/Desktop/Program.cs @@ -39,7 +39,7 @@ monoGameWindow.Graphics.PreferredBackBufferWidth = clientConfiguration.windowWid monoGameWindow.Graphics.PreferredBackBufferHeight = clientConfiguration.windowHeight; monoGameWindow.Graphics.GraphicsProfile = GraphicsProfile.HiDef; -universe.FindBehaviour()? +universe.FindBehaviour()? .OnConnectionEstablished.AddOneTimeListener( (sender, connection) => monoGameWindow.Window.Title = $"Client {connection.Id}" ); diff --git a/Platforms/Server/PongEndpoints.cs b/Platforms/Server/PongEndpoints.cs index 484d278..0999964 100644 --- a/Platforms/Server/PongEndpoints.cs +++ b/Platforms/Server/PongEndpoints.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Syntriax.Engine.Core; -using Syntriax.Engine.Network; +using Syntriax.Engine.Systems.Network; namespace Server; diff --git a/Pong.sln b/Pong.sln index ddfb00f..6c8d383 100644 --- a/Pong.sln +++ b/Pong.sln @@ -29,6 +29,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Serializers", "Engin EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Serializers.Yaml", "Engine\Engine.Serializers\Engine.Serializers.Yaml\Engine.Serializers.Yaml.csproj", "{79F870AB-249E-4CA0-9DF0-F265514581DF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.LiteNetLib", "Engine\Engine.Integration\Engine.Integration.LiteNetLib\Engine.Integration.LiteNetLib.csproj", "{7AA22306-772F-45F4-8F30-97EBD1FC124D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -147,6 +149,18 @@ Global {79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|x64.Build.0 = Release|Any CPU {79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|x86.ActiveCfg = Release|Any CPU {79F870AB-249E-4CA0-9DF0-F265514581DF}.Release|x86.Build.0 = Release|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Debug|x64.ActiveCfg = Debug|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Debug|x64.Build.0 = Debug|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Debug|x86.ActiveCfg = Debug|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Debug|x86.Build.0 = Debug|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Release|Any CPU.Build.0 = Release|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Release|x64.ActiveCfg = Release|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Release|x64.Build.0 = Release|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Release|x86.ActiveCfg = Release|Any CPU + {7AA22306-772F-45F4-8F30-97EBD1FC124D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -162,5 +176,6 @@ Global {A15263DB-DF65-4A07-8CA1-33A2919501A0} = {FECFFD54-338F-4060-9161-1E5770D1DC33} {F1257AE1-A8BC-DE44-CB86-406F1335A1D0} = {F7F62670-237A-4C93-A30E-CE661C6FC401} {79F870AB-249E-4CA0-9DF0-F265514581DF} = {F1257AE1-A8BC-DE44-CB86-406F1335A1D0} + {7AA22306-772F-45F4-8F30-97EBD1FC124D} = {9059393F-4073-9273-0EEC-2B1BA61B620B} EndGlobalSection EndGlobal diff --git a/Shared/Behaviours/Ball.cs b/Shared/Behaviours/Ball.cs index 613a0b0..feae8f8 100644 --- a/Shared/Behaviours/Ball.cs +++ b/Shared/Behaviours/Ball.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Content; using Syntriax.Engine.Core; using Syntriax.Engine.Integration.MonoGame; -using Syntriax.Engine.Network; +using Syntriax.Engine.Systems.Network; using Syntriax.Engine.Physics2D; using Syntriax.Engine.Systems.Tween; diff --git a/Shared/Behaviours/Paddle.cs b/Shared/Behaviours/Paddle.cs index fc1fe05..cec6a69 100644 --- a/Shared/Behaviours/Paddle.cs +++ b/Shared/Behaviours/Paddle.cs @@ -3,7 +3,7 @@ using System; using Microsoft.Xna.Framework.Input; using Syntriax.Engine.Core; -using Syntriax.Engine.Network; +using Syntriax.Engine.Systems.Network; using Syntriax.Engine.Physics2D; using Syntriax.Engine.Systems.Input; using Syntriax.Engine.Systems.Tween; diff --git a/Shared/Behaviours/PongGameStarter.cs b/Shared/Behaviours/PongGameStarter.cs index ff6fad3..35703f9 100644 --- a/Shared/Behaviours/PongGameStarter.cs +++ b/Shared/Behaviours/PongGameStarter.cs @@ -5,7 +5,7 @@ using Microsoft.Xna.Framework.Input; using Syntriax.Engine.Core; using Syntriax.Engine.Core.Debug; using Syntriax.Engine.Integration.MonoGame; -using Syntriax.Engine.Network; +using Syntriax.Engine.Systems.Network; using Syntriax.Engine.Systems.Input; using Syntriax.Engine.Systems.Time; using Syntriax.Engine.Systems.Tween; diff --git a/Shared/Behaviours/PongManager.cs b/Shared/Behaviours/PongManager.cs index f5c58f1..fd8438a 100644 --- a/Shared/Behaviours/PongManager.cs +++ b/Shared/Behaviours/PongManager.cs @@ -6,7 +6,7 @@ using Microsoft.Xna.Framework.Content; using Syntriax.Engine.Core; using Syntriax.Engine.Core.Debug; using Syntriax.Engine.Integration.MonoGame; -using Syntriax.Engine.Network; +using Syntriax.Engine.Systems.Network; namespace Pong.Behaviours; diff --git a/Shared/Network/Abstract/IConnection.cs b/Shared/Network/Abstract/IConnection.cs deleted file mode 100644 index a477434..0000000 --- a/Shared/Network/Abstract/IConnection.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Syntriax.Engine.Network; - -public interface IConnection -{ - string Id { get; } - - float Ping { get; } - float RoundTrip { get; } - - int PingMs { get; } - int RoundTripMs { get; } -} diff --git a/Shared/Network/Abstract/IEntityNetworkPacket.cs b/Shared/Network/Abstract/IEntityNetworkPacket.cs deleted file mode 100644 index 70f0696..0000000 --- a/Shared/Network/Abstract/IEntityNetworkPacket.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Syntriax.Engine.Network; - -public interface IEntityNetworkPacket : INetworkPacket -{ - string EntityId { get; } -} diff --git a/Shared/Network/Abstract/INetworkCommunicator.cs b/Shared/Network/Abstract/INetworkCommunicator.cs deleted file mode 100644 index 20ed5f2..0000000 --- a/Shared/Network/Abstract/INetworkCommunicator.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -public interface INetworkCommunicator -{ - Event OnConnectionEstablished { get; } - Event OnConnectionAbolished { get; } - - IReadOnlyDictionary Connections { get; } - - INetworkCommunicator Stop(); - - INetworkCommunicator SubscribeToPackets(Event.EventHandler callback); - INetworkCommunicator UnsubscribeFromPackets(Event.EventHandler callback); -} - -public interface INetworkCommunicatorClient : INetworkCommunicator -{ - INetworkCommunicatorClient Connect(string address, int port, string? password = null); - - INetworkCommunicatorClient SendToServer(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new(); -} - -public interface INetworkCommunicatorServer : INetworkCommunicator -{ - string Password { get; } - int MaxConnectionCount { get; } - int Port { get; } - - INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null); - - INetworkCommunicatorServer SendToClient(IConnection connection, T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new(); - INetworkCommunicatorServer SendToAll(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new(); -} diff --git a/Shared/Network/Abstract/INetworkEntity.cs b/Shared/Network/Abstract/INetworkEntity.cs deleted file mode 100644 index 6d874e9..0000000 --- a/Shared/Network/Abstract/INetworkEntity.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -public interface INetworkEntity : IEntity; diff --git a/Shared/Network/Abstract/INetworkManager.cs b/Shared/Network/Abstract/INetworkManager.cs deleted file mode 100644 index 91c1b4f..0000000 --- a/Shared/Network/Abstract/INetworkManager.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -public interface INetworkManager -{ - IReadOnlyDictionary NetworkEntities { get; } - IBehaviourCollector NetworkEntityCollector { get; } -} diff --git a/Shared/Network/Abstract/INetworkPacket.cs b/Shared/Network/Abstract/INetworkPacket.cs deleted file mode 100644 index 2e56821..0000000 --- a/Shared/Network/Abstract/INetworkPacket.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Syntriax.Engine.Network; - -public interface INetworkPacket; diff --git a/Shared/Network/Abstract/IPacketListenerClient.cs b/Shared/Network/Abstract/IPacketListenerClient.cs deleted file mode 100644 index a9e1a33..0000000 --- a/Shared/Network/Abstract/IPacketListenerClient.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Syntriax.Engine.Network; - -public interface IPacketListenerClient : INetworkEntity -{ - void OnClientPacketArrived(IConnection sender, T packet); -} diff --git a/Shared/Network/Abstract/IPacketListenerServer.cs b/Shared/Network/Abstract/IPacketListenerServer.cs deleted file mode 100644 index c1dae03..0000000 --- a/Shared/Network/Abstract/IPacketListenerServer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Syntriax.Engine.Network; - -public interface IPacketListenerServer : INetworkEntity -{ - void OnServerPacketArrived(IConnection sender, T packet); -} diff --git a/Shared/Network/Abstract/PacketDelivery.cs b/Shared/Network/Abstract/PacketDelivery.cs deleted file mode 100644 index 36f307d..0000000 --- a/Shared/Network/Abstract/PacketDelivery.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Syntriax.Engine.Network; - -public enum PacketDelivery -{ - ReliableInOrder, - ReliableOutOfOrder, - UnreliableInOrder, - UnreliableOutOfOrder, -}; diff --git a/Shared/Network/LiteNetLib/LiteNetLibClient.cs b/Shared/Network/LiteNetLib/LiteNetLibClient.cs deleted file mode 100644 index 95af97b..0000000 --- a/Shared/Network/LiteNetLib/LiteNetLibClient.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Linq; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; - -using LiteNetLib; -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Debug; - -namespace Syntriax.Engine.Network; - -public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicatorClient -{ - private readonly NetDataWriter netDataWriter = new(); - - private CancellationTokenSource? cancellationTokenSource = null; - - protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibServerConnection(peer); - - public INetworkCommunicatorClient Connect(string address, int port, string? password = null) - { - if (!UniverseObject.IsInUniverse) - throw new($"{nameof(LiteNetLibClient)} must be in an universe to connect"); - - password ??= string.Empty; - - // Okay, for some reason sometimes LiteNetLib goes dumb when the server hostname has IPv6 address as well as IPv4 - // but the client doesn't support IPv6, it still tries to use the v6 unless we explicitly tell the ip to connect to, - // which fails to connect... So for the time being I am preferring IPv4 below over IPv6 for clients. - // TODO: I think this is something that happens on Linux only? I need to check on Windows as well just to be sure. - System.Net.IPAddress[] addresses = System.Net.Dns.GetHostAddresses(address); - string connectionAddress = addresses.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork, addresses[0]).ToString(); - - logger?.Log(this, $"Connecting to server at '{address}:{port}' with password '{password}'"); - logger?.Log(this, $"Resolved address for {address}: {connectionAddress}"); - - Manager.Start(); - Manager.Connect(connectionAddress, port, password); - - return this; - } - - public INetworkCommunicatorClient SendToServer(T packet, PacketDelivery packetDelivery) where T : class, new() - { - netDataWriter.Reset(); - netPacketProcessor.Write(netDataWriter, packet); - - switch (packetDelivery) - { - case PacketDelivery.ReliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; - case PacketDelivery.UnreliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break; - case PacketDelivery.ReliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break; - case PacketDelivery.UnreliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break; - default: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; - } - - return this; - } - - protected override void OnEnteredUniverse(IUniverse universe) - { - base.OnEnteredUniverse(universe); - - cancellationTokenSource = new CancellationTokenSource(); - PollEvents(cancellationTokenSource.Token); - } - - protected override void OnExitedUniverse(IUniverse universe) - { - base.OnExitedUniverse(universe); - cancellationTokenSource?.Cancel(); - } - - /// - /// Client needs to send everything as soon as possible so - /// the events are polled a separate thread running constantly - /// - private async void PollEvents(CancellationToken cancellationToken) => await Task.Run(() => - { - while (true) - { - Manager.PollEvents(); - Thread.Sleep(1); - } - }, cancellationToken); -} diff --git a/Shared/Network/LiteNetLib/LiteNetLibClientConnection.cs b/Shared/Network/LiteNetLib/LiteNetLibClientConnection.cs deleted file mode 100644 index add84ad..0000000 --- a/Shared/Network/LiteNetLib/LiteNetLibClientConnection.cs +++ /dev/null @@ -1,16 +0,0 @@ -using LiteNetLib; - -namespace Syntriax.Engine.Network; - -public record class LiteNetLibClientConnection(NetPeer NetPeer) : IConnection -{ - public string Id { get; } = NetPeer.Id.ToString(); - - public float Ping => NetPeer.Ping * .001f; - public float RoundTrip => NetPeer.RoundTripTime * .001f; - - public int PingMs => NetPeer.Ping; - public int RoundTripMs => NetPeer.RoundTripTime; - - public override string ToString() => $"Connection({Id})"; -} diff --git a/Shared/Network/LiteNetLib/LiteNetLibCommunicatorBase.cs b/Shared/Network/LiteNetLib/LiteNetLibCommunicatorBase.cs deleted file mode 100644 index 03a2b39..0000000 --- a/Shared/Network/LiteNetLib/LiteNetLibCommunicatorBase.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using LiteNetLib; -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Debug; - -namespace Syntriax.Engine.Network; - -public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicator -{ - protected readonly NetPacketProcessor netPacketProcessor = new(); - - private readonly Dictionary> listeners = []; - private readonly Dictionary _connections = []; - - private readonly Dictionary localPeerIdToConnectionDictionary = []; - - protected ILogger? logger = null; - - public IReadOnlyDictionary Connections => _connections; - public EventBasedNetListener Listener { get; private set; } = null!; - public NetManager Manager { get; private set; } = null!; - - public Event OnConnectionEstablished { get; } = new(); - public Event OnConnectionAbolished { get; } = new(); - - public INetworkCommunicator Stop() - { - Manager.Stop(); - return this; - } - - protected override void OnEnteredUniverse(IUniverse universe) - { - base.OnEnteredUniverse(universe); - logger = universe.FindBehaviour(); - } - - protected override void OnExitedUniverse(IUniverse universe) - { - base.OnExitedUniverse(universe); - logger = null; - Stop(); - } - - protected virtual void OnPacketArrived(T packet, NetPeer peer) where T : INetworkPacket - { - if (!listeners.TryGetValue(typeof(T), out Event? @event)) - return; - - @event.Invoke(localPeerIdToConnectionDictionary[peer.Id], packet); - } - - private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) - { - try { netPacketProcessor.ReadAllPackets(reader, peer); } - catch (Exception exception) { logger?.LogException(this, exception, force: true); } - } - - protected abstract IConnection GetConnection(NetPeer peer); - private void ConnectionEstablished(NetPeer peer) - { - IConnection connection = GetConnection(peer); - - localPeerIdToConnectionDictionary.Add(peer.Id, connection); - _connections.Add(connection.Id, connection); - - logger?.Log(this, $"Connection established with ip '{peer.Address}' and id '{connection.Id}'"); - OnConnectionEstablished.Invoke(this, connection); - } - - - private void ConnectionAbolished(NetPeer peer, DisconnectInfo disconnectInfo) - { - if (!localPeerIdToConnectionDictionary.TryGetValue(peer.Id, out var connection)) - return; - - localPeerIdToConnectionDictionary.Remove(peer.Id); - _connections.Remove(connection.Id); - - logger?.Log(this, $"Connection abolished with ip '{peer.Address}' and id '{connection.Id}'"); - OnConnectionAbolished.Invoke(this, connection); - } - - private void SetupPackets() - { - // 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 subscribeReusableMethod = typeof(NetPacketProcessor) - .GetMethods() - .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable) && - m.GetParameters().Length == 1 && - m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) - )!; - - MethodInfo registerNestedTypeMethod = typeof(NetPacketProcessor) - .GetMethods() - .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.RegisterNestedType) && - m.GetParameters().Length == 0 - )!; - - MethodInfo onPacketArrivedMethod = typeof(LiteNetLibCommunicatorBase) - .GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance)!; - - // Register all network packets by calling the methods bellow where T is our found network packet type - // NetPacketProcessor.SubscribeReusable(Action onReceive) - // NetPacketProcessor.RegisterNestedType() - foreach (Type packetType in packetTypes) - { - if (!packetType.IsClass) - { - MethodInfo genericRegisterNestedTypeMethod = registerNestedTypeMethod.MakeGenericMethod(packetType); - genericRegisterNestedTypeMethod.Invoke(netPacketProcessor, []); - continue; - } - - MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType); - - Type delegateType = typeof(Action<,>).MakeGenericType(packetType, typeof(NetPeer)); - Delegate delegateHandler = Delegate.CreateDelegate(delegateType, this, genericOnPacketArrivedMethod); - - var genericSubscribeReusableMethod = subscribeReusableMethod.MakeGenericMethod(packetType, typeof(NetPeer)); - genericSubscribeReusableMethod.Invoke(netPacketProcessor, [delegateHandler]); - } - } - - public LiteNetLibCommunicatorBase() - { - Listener = new EventBasedNetListener(); - Manager = new NetManager(Listener); - - Listener.NetworkReceiveEvent += NetworkReceiveEvent; - Listener.PeerConnectedEvent += ConnectionEstablished; - Listener.PeerDisconnectedEvent += ConnectionAbolished; - - SetupEnginePackets(); - SetupPackets(); - } - - private void SetupEnginePackets() - { - // I know, ugly af. I need to find a better way - netPacketProcessor.RegisterNestedType(AABBNetPacker.Write, AABBNetPacker.Read); - netPacketProcessor.RegisterNestedType(CircleNetPacker.Write, CircleNetPacker.Read); - netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.Read); - netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read); - netPacketProcessor.RegisterNestedType(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read); - netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read); - netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.Read); - netPacketProcessor.RegisterNestedType(Projection1DNetPacker.Write, Projection1DNetPacker.Read); - netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.Read); - netPacketProcessor.RegisterNestedType(Shape2DNetPacker.Write, Shape2DNetPacker.Read); - netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read); - netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read); - netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.Read); - } - - public INetworkCommunicator SubscribeToPackets(Event.EventHandler callback) - { - Type type = typeof(T); - if (!listeners.TryGetValue(type, out Event? @event)) - { - @event = new(); - listeners.Add(type, @event); - } - - @event.AddListener(EventDelegateWrapper(callback)); - return this; - } - - public INetworkCommunicator UnsubscribeFromPackets(Event.EventHandler callback) - { - Type type = typeof(T); - if (!listeners.TryGetValue(type, out Event? @event)) - return this; - - @event.RemoveListener(EventDelegateWrapper(callback)); - return this; - } - - private static Event.EventHandler EventDelegateWrapper(Event.EventHandler callback) => (sender, @object) => callback.Invoke(sender, (T)@object); -} diff --git a/Shared/Network/LiteNetLib/LiteNetLibServer.cs b/Shared/Network/LiteNetLib/LiteNetLibServer.cs deleted file mode 100644 index 482b49f..0000000 --- a/Shared/Network/LiteNetLib/LiteNetLibServer.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Linq; - -using LiteNetLib; -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Debug; - -namespace Syntriax.Engine.Network; - -public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicatorServer -{ - public string Password { get; private set; } = string.Empty; - public int MaxConnectionCount { get; private set; } = 2; - public int Port { get; private set; } = 8888; - - private readonly NetDataWriter netDataWriter = new(); - - public LiteNetLibServer() : this(8888, 2) { } - public LiteNetLibServer(int port, int maxConnectionCount) : base() - { - MaxConnectionCount = maxConnectionCount; - Port = port; - - Listener.ConnectionRequestEvent += OnConnectionRequest; - } - - protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibClientConnection(peer); - - private void OnConnectionRequest(ConnectionRequest request) - { - logger?.Log(this, $"Connection request from ip {request.RemoteEndPoint}"); - logger?.Log(this, $"Current connection count: {Connections.Count}"); - if (Manager.ConnectedPeersCount < MaxConnectionCount) - request.AcceptIfKey(Password); - else - request.Reject(); - } - - public INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null) - { - if (!UniverseObject.IsInUniverse) - throw new($"{nameof(LiteNetLibServer)} must be in an universe to start"); - - Password = password ?? string.Empty; - MaxConnectionCount = maxConnectionCount; - Port = port; - - logger?.Log(this, $"Starting server on port '{port}' with password '{Password}' and max connection count '{maxConnectionCount}'"); - logger?.Log(this, $"Server status: {(Manager.Start(port) ? "Active" : "Failed")}"); - - return this; - } - - public INetworkCommunicatorServer SendToClient(IConnection connection, T packet, PacketDelivery packetDelivery) where T : class, new() - { - netDataWriter.Reset(); - netPacketProcessor.Write(netDataWriter, packet); - - if (Manager.ConnectedPeerList.FirstOrDefault(p => p.Id.CompareTo(connection.Id) == 0) is not NetPeer netPeer) - throw new($"Peer {connection} couldn't be found."); - - switch (packetDelivery) - { - case PacketDelivery.ReliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; - case PacketDelivery.UnreliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break; - case PacketDelivery.ReliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break; - case PacketDelivery.UnreliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break; - default: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; - } - - return this; - } - - public INetworkCommunicatorServer SendToAll(T packet, PacketDelivery packetDelivery) where T : class, new() - { - netDataWriter.Reset(); - netPacketProcessor.Write(netDataWriter, packet); - - switch (packetDelivery) - { - case PacketDelivery.ReliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break; - case PacketDelivery.UnreliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Sequenced); break; - case PacketDelivery.ReliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableUnordered); break; - case PacketDelivery.UnreliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Unreliable); break; - default: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break; - } - return this; - } - - private void PollEvents(IUniverse sender, IUniverse.UpdateArguments args) => Manager.PollEvents(); - - protected override void OnEnteredUniverse(IUniverse universe) - { - base.OnEnteredUniverse(universe); - universe.OnPostUpdate.AddListener(PollEvents); - } - - protected override void OnExitedUniverse(IUniverse universe) - { - base.OnExitedUniverse(universe); - universe.OnPostUpdate.RemoveListener(PollEvents); - } -} diff --git a/Shared/Network/LiteNetLib/LiteNetLibServerConnection.cs b/Shared/Network/LiteNetLib/LiteNetLibServerConnection.cs deleted file mode 100644 index 783698f..0000000 --- a/Shared/Network/LiteNetLib/LiteNetLibServerConnection.cs +++ /dev/null @@ -1,16 +0,0 @@ -using LiteNetLib; - -namespace Syntriax.Engine.Network; - -public record class LiteNetLibServerConnection(NetPeer NetPeer) : IConnection -{ - public string Id => NetPeer.RemoteId.ToString(); - - public float Ping => NetPeer.Ping * .001f; - public float RoundTrip => NetPeer.RoundTripTime * .001f; - - public int PingMs => NetPeer.Ping; - public int RoundTripMs => NetPeer.RoundTripTime; - - public override string ToString() => $"Connection({Id})"; -} diff --git a/Shared/Network/LiteNetLib/Packers/AABBNetPacker.cs b/Shared/Network/LiteNetLib/Packers/AABBNetPacker.cs deleted file mode 100644 index 6372cd9..0000000 --- a/Shared/Network/LiteNetLib/Packers/AABBNetPacker.cs +++ /dev/null @@ -1,22 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class AABBNetPacker -{ - internal static void Write(NetDataWriter writer, AABB data) - { - Vector2DNetPacker.Write(writer, data.LowerBoundary); - Vector2DNetPacker.Write(writer, data.UpperBoundary); - } - - internal static AABB Read(NetDataReader reader) - { - Vector2D lowerBoundary = Vector2DNetPacker.Read(reader); - Vector2D upperBoundary = Vector2DNetPacker.Read(reader); - - return new AABB(lowerBoundary, upperBoundary); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/CircleNetPacker.cs b/Shared/Network/LiteNetLib/Packers/CircleNetPacker.cs deleted file mode 100644 index 7c323d8..0000000 --- a/Shared/Network/LiteNetLib/Packers/CircleNetPacker.cs +++ /dev/null @@ -1,22 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class CircleNetPacker -{ - internal static void Write(NetDataWriter writer, Circle data) - { - Vector2DNetPacker.Write(writer, data.Center); - writer.Put(data.Radius); - } - - internal static Circle Read(NetDataReader reader) - { - Vector2D center = Vector2DNetPacker.Read(reader); - float radius = reader.GetFloat(); - - return new Circle(center, radius); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/ColorHSVNetPacker.cs b/Shared/Network/LiteNetLib/Packers/ColorHSVNetPacker.cs deleted file mode 100644 index 3580923..0000000 --- a/Shared/Network/LiteNetLib/Packers/ColorHSVNetPacker.cs +++ /dev/null @@ -1,24 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class ColorHSVNetPacker -{ - internal static void Write(NetDataWriter writer, ColorHSV data) - { - writer.Put(data.Hue); - writer.Put(data.Saturation); - writer.Put(data.Value); - } - - internal static ColorHSV Read(NetDataReader reader) - { - float hue = reader.GetFloat(); - float saturation = reader.GetFloat(); - float value = reader.GetFloat(); - - return new ColorHSV(hue, saturation, value); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/ColorRGBANetPacker.cs b/Shared/Network/LiteNetLib/Packers/ColorRGBANetPacker.cs deleted file mode 100644 index d5f6b89..0000000 --- a/Shared/Network/LiteNetLib/Packers/ColorRGBANetPacker.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class ColorRGBANetPacker -{ - internal static void Write(NetDataWriter writer, ColorRGBA data) - { - writer.Put(data.R); - writer.Put(data.G); - writer.Put(data.B); - writer.Put(data.A); - } - - internal static ColorRGBA Read(NetDataReader reader) - { - byte red = reader.GetByte(); - byte green = reader.GetByte(); - byte blue = reader.GetByte(); - byte alpha = reader.GetByte(); - - return new ColorRGBA(red, green, blue, alpha); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/ColorRGBNetPacker.cs b/Shared/Network/LiteNetLib/Packers/ColorRGBNetPacker.cs deleted file mode 100644 index b9fed97..0000000 --- a/Shared/Network/LiteNetLib/Packers/ColorRGBNetPacker.cs +++ /dev/null @@ -1,24 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class ColorRGBNetPacker -{ - internal static void Write(NetDataWriter writer, ColorRGB data) - { - writer.Put(data.R); - writer.Put(data.G); - writer.Put(data.B); - } - - internal static ColorRGB Read(NetDataReader reader) - { - byte red = reader.GetByte(); - byte green = reader.GetByte(); - byte blue = reader.GetByte(); - - return new ColorRGB(red, green, blue); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/Line2DEquationNetPacker.cs b/Shared/Network/LiteNetLib/Packers/Line2DEquationNetPacker.cs deleted file mode 100644 index dc770f6..0000000 --- a/Shared/Network/LiteNetLib/Packers/Line2DEquationNetPacker.cs +++ /dev/null @@ -1,22 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class Line2DEquationNetPacker -{ - internal static void Write(NetDataWriter writer, Line2DEquation data) - { - writer.Put(data.Slope); - writer.Put(data.OffsetY); - } - - internal static Line2DEquation Read(NetDataReader reader) - { - float slope = reader.GetFloat(); - float offsetY = reader.GetFloat(); - - return new Line2DEquation(slope, offsetY); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/Line2DNetPacker.cs b/Shared/Network/LiteNetLib/Packers/Line2DNetPacker.cs deleted file mode 100644 index f64b0b3..0000000 --- a/Shared/Network/LiteNetLib/Packers/Line2DNetPacker.cs +++ /dev/null @@ -1,22 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class Line2DNetPacker -{ - internal static void Write(NetDataWriter writer, Line2D data) - { - Vector2DNetPacker.Write(writer, data.From); - Vector2DNetPacker.Write(writer, data.To); - } - - internal static Line2D Read(NetDataReader reader) - { - Vector2D from = Vector2DNetPacker.Read(reader); - Vector2D to = Vector2DNetPacker.Read(reader); - - return new Line2D(from, to); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/Projection1DNetPacker.cs b/Shared/Network/LiteNetLib/Packers/Projection1DNetPacker.cs deleted file mode 100644 index 4307423..0000000 --- a/Shared/Network/LiteNetLib/Packers/Projection1DNetPacker.cs +++ /dev/null @@ -1,22 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class Projection1DNetPacker -{ - internal static void Write(NetDataWriter writer, Projection1D data) - { - writer.Put(data.Min); - writer.Put(data.Max); - } - - internal static Projection1D Read(NetDataReader reader) - { - float min = reader.GetFloat(); - float max = reader.GetFloat(); - - return new Projection1D(min, max); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/QuaternionNetPacker.cs b/Shared/Network/LiteNetLib/Packers/QuaternionNetPacker.cs deleted file mode 100644 index 63d7afa..0000000 --- a/Shared/Network/LiteNetLib/Packers/QuaternionNetPacker.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class QuaternionNetPacker -{ - internal static void Write(NetDataWriter writer, Quaternion data) - { - writer.Put(data.X); - writer.Put(data.Y); - writer.Put(data.Z); - writer.Put(data.W); - } - - internal static Quaternion Read(NetDataReader reader) - { - float x = reader.GetFloat(); - float y = reader.GetFloat(); - float z = reader.GetFloat(); - float w = reader.GetFloat(); - - return new Quaternion(x, y, z, w); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/Shape2DNetPacker.cs b/Shared/Network/LiteNetLib/Packers/Shape2DNetPacker.cs deleted file mode 100644 index a4378cd..0000000 --- a/Shared/Network/LiteNetLib/Packers/Shape2DNetPacker.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; - -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class Shape2DNetPacker -{ - internal static void Write(NetDataWriter writer, Shape2D data) - { - writer.Put(data.Vertices.Count); - foreach (Vector2D vertex in data.Vertices) - Vector2DNetPacker.Write(writer, vertex); - } - - internal static Shape2D Read(NetDataReader reader) - { - int verticesCount = reader.GetInt(); - List vertices = new(verticesCount); - - for (int i = 0; i < verticesCount; i++) - vertices.Add(Vector2DNetPacker.Read(reader)); - - return new Shape2D(vertices); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/TriangleNetPacker.cs b/Shared/Network/LiteNetLib/Packers/TriangleNetPacker.cs deleted file mode 100644 index 0e052e3..0000000 --- a/Shared/Network/LiteNetLib/Packers/TriangleNetPacker.cs +++ /dev/null @@ -1,24 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class TriangleNetPacker -{ - internal static void Write(NetDataWriter writer, Triangle data) - { - Vector2DNetPacker.Write(writer, data.A); - Vector2DNetPacker.Write(writer, data.B); - Vector2DNetPacker.Write(writer, data.C); - } - - internal static Triangle Read(NetDataReader reader) - { - Vector2D a = Vector2DNetPacker.Read(reader); - Vector2D b = Vector2DNetPacker.Read(reader); - Vector2D c = Vector2DNetPacker.Read(reader); - - return new Triangle(a, b, c); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/Vector2DNetPacker.cs b/Shared/Network/LiteNetLib/Packers/Vector2DNetPacker.cs deleted file mode 100644 index 091e80a..0000000 --- a/Shared/Network/LiteNetLib/Packers/Vector2DNetPacker.cs +++ /dev/null @@ -1,22 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class Vector2DNetPacker -{ - internal static void Write(NetDataWriter writer, Vector2D data) - { - writer.Put(data.X); - writer.Put(data.Y); - } - - internal static Vector2D Read(NetDataReader reader) - { - float x = reader.GetFloat(); - float y = reader.GetFloat(); - - return new Vector2D(x, y); - } -} diff --git a/Shared/Network/LiteNetLib/Packers/Vector3DNetPacker.cs b/Shared/Network/LiteNetLib/Packers/Vector3DNetPacker.cs deleted file mode 100644 index c75bcce..0000000 --- a/Shared/Network/LiteNetLib/Packers/Vector3DNetPacker.cs +++ /dev/null @@ -1,24 +0,0 @@ -using LiteNetLib.Utils; - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Network; - -internal static class Vector3DNetPacker -{ - internal static void Write(NetDataWriter writer, Vector3D data) - { - writer.Put(data.X); - writer.Put(data.Y); - writer.Put(data.Z); - } - - internal static Vector3D Read(NetDataReader reader) - { - float x = reader.GetFloat(); - float y = reader.GetFloat(); - float z = reader.GetFloat(); - - return new Vector3D(x, y, z); - } -} diff --git a/Shared/Network/NetworkManager.cs b/Shared/Network/NetworkManager.cs deleted file mode 100644 index a84c485..0000000 --- a/Shared/Network/NetworkManager.cs +++ /dev/null @@ -1,333 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -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 : Behaviour, INetworkManager -{ - private readonly Dictionary>> clientPacketArrivalMethods = []; - private readonly Dictionary>> serverPacketArrivalMethods = []; - - private readonly Dictionary> clientPacketRouters = []; - private readonly Dictionary> serverPacketRouters = []; - - private readonly List<(Type packetType, Delegate @delegate)> packetRetrievalDelegates = []; - - private readonly Dictionary clearRoutesMethods = []; - private readonly Dictionary registerPacketListenersMethods = []; - - private readonly Dictionary _networkEntities = []; - public IReadOnlyDictionary NetworkEntities => _networkEntities; - - 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) UnsubscribeCommunicatorMethods(previousCommunicator); - if (_networkCommunicator is not null) SubscribeCommunicatorMethods(_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 @delegate) in packetRetrievalDelegates) - { - MethodInfo genericSubscribeMethod = subscribeToPacketsMethod.MakeGenericMethod(packetType); - genericSubscribeMethod.Invoke(networkCommunicator, [@delegate]); - } - } - - private void UnsubscribeCommunicatorMethods(INetworkCommunicator networkCommunicator) - { - MethodInfo unsubscribeFromPacketsMethod = typeof(INetworkCommunicator) - .GetMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!; - - foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates) - { - MethodInfo genericUnsubscribeMethod = unsubscribeFromPacketsMethod.MakeGenericMethod(packetType); - genericUnsubscribeMethod.Invoke(networkCommunicator, [@delegate]); - } - } - #endregion - - #region Packet Routing - private void OnPacketReceived(IConnection sender, T entityDataPacket) - { - if (entityDataPacket is IEntityNetworkPacket entityPacket) - RoutePacket(sender, entityDataPacket, entityPacket); - else - BroadcastPacket(sender, entityDataPacket); - } - - private void RoutePacket(IConnection sender, T entityDataPacket, IEntityNetworkPacket entityPacket) - { - if (NetworkCommunicator is INetworkCommunicatorClient) - RoutePacket(clientPacketRouters, entityPacket.EntityId, sender, entityDataPacket); - if (NetworkCommunicator is INetworkCommunicatorServer) - RoutePacket(serverPacketRouters, entityPacket.EntityId, sender, entityDataPacket); - } - - private void BroadcastPacket(IConnection sender, T entityDataPacket) - { - if (NetworkCommunicator is INetworkCommunicatorClient) - BroadcastPacket(clientPacketRouters, sender, entityDataPacket); - if (NetworkCommunicator is INetworkCommunicatorServer) - BroadcastPacket(serverPacketRouters, sender, entityDataPacket); - } - - - private static void BroadcastPacket( - Dictionary> packetRouters, - IConnection sender, - T entityDataPacket) - { - if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary? routers)) - return; - - foreach ((string behaviourId, object routerEventReference) in routers) - { - Event routerEvent = (Event)routerEventReference; - routerEvent.Invoke(sender, entityDataPacket!); - } - } - - private static void RoutePacket( - Dictionary> packetRouters, - string entityId, - IConnection sender, - T entityDataPacket) - { - if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary? routers)) - return; - - if (!routers.TryGetValue(entityId, out object? routerEventReference)) - return; - - Event routerEvent = (Event)routerEventReference; - routerEvent.Invoke(sender, entityDataPacket!); - } - #endregion - - #region Packet Routers - private 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); - } - - object packetListenerEvent = CreateEventAndRegister(packetType, behaviour, networkType); - routers.Add(behaviour.Id, packetListenerEvent); - } - } - - private object CreateEventAndRegister(Type packetType, INetworkEntity behaviour, NetworkType networkType) - { - Type genericEventType = typeof(Event<,>).MakeGenericType(typeof(IConnection), packetType); - object packetListenerEvent = Activator.CreateInstance(genericEventType)!; - - if (!registerPacketListenersMethods.TryGetValue(packetType, out MethodInfo? registerPacketListenerMethod)) - throw new($"{nameof(RegisterPacketListenerEvent)} for {packetType.Name} has not been cached."); - - registerPacketListenerMethod.Invoke(this, [behaviour, packetListenerEvent, networkType]); - return packetListenerEvent; - } - - private static void RegisterPacketListenerEvent( - 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; - } - } - - private 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 object? routerEventReference)) - continue; - - if (!clearRoutesMethods.TryGetValue(packetType, out MethodInfo? clearRouterMethod)) - continue; - - clearRouterMethod.Invoke(this, [routerEventReference]); - } - } - - private static void ClearRouter(object routerEventReference) - { - Event routerEvent = (Event)routerEventReference; - 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 OnExitedUniverse(IUniverse universe) => _networkEntityCollector.Unassign(); - protected override void OnEnteredUniverse(IUniverse universe) - { - _networkEntityCollector.Assign(universe); - NetworkCommunicator = BehaviourController.GetRequiredBehaviourInParent(); - } - #endregion - - #region Initialization - public NetworkManager() - { - CachePacketRetrievalDelegates(); - CacheRegistrationMethods(); - CachePacketArrivalMethods(); - - _networkEntityCollector.OnCollected.AddListener(OnCollected); - _networkEntityCollector.OnRemoved.AddListener(OnRemoved); - } - - private void CachePacketRetrievalDelegates() - { - 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(IConnection), packetType); - Delegate genericPacketReceivedDelegate = Delegate.CreateDelegate(genericDelegateType, this, genericOnPacketArrivedMethod); - - packetRetrievalDelegates.Add((packetType, genericPacketReceivedDelegate)); - } - } - - private void CacheRegistrationMethods() - { - CacheRegistrationMethods(registerPacketListenersMethods, nameof(RegisterPacketListenerEvent)); - CacheRegistrationMethods(clearRoutesMethods, nameof(ClearRouter)); - } - - private void CacheRegistrationMethods(Dictionary registrationMethods, string methodName) - { - MethodInfo registerPacketMethod = typeof(NetworkManager).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static)!; - foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates) - { - MethodInfo genericMethod = registerPacketMethod.MakeGenericMethod(packetType); - registrationMethods.Add(packetType, genericMethod); - } - } - - private void CachePacketArrivalMethods() - { - CachePacketArrivalMethods(clientPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient.OnClientPacketArrived)); - CachePacketArrivalMethods(serverPacketArrivalMethods, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer.OnServerPacketArrived)); - } - - private static void CachePacketArrivalMethods(Dictionary>> packetArrivalMethods, Type listenerType, string packetArrivalMethodName) - { - foreach (Type listenerClass in GetGenericsWith(listenerType)) - { - Dictionary> packetRouters = []; - packetArrivalMethods.Add(listenerClass, packetRouters); - - 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 GetGenericsWith(Type type) - => AppDomain.CurrentDomain - .GetAssemblies() - .SelectMany(a => - a.GetTypes().Where( - t => t.GetInterfaces().Any( - i => i.IsGenericType && i.GetGenericTypeDefinition() == type - ) - ) - ); - - private static IEnumerable GetGenericInterfacesWith(Type interfaceType, Type type) - => type.GetInterfaces().Where( - i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType - ); - #endregion - - private enum NetworkType { Client, Server } -} diff --git a/Shared/PongUniverse.cs b/Shared/PongUniverse.cs index 6c4ed5c..8c1a350 100644 --- a/Shared/PongUniverse.cs +++ b/Shared/PongUniverse.cs @@ -7,7 +7,7 @@ using Pong.Behaviours; using Syntriax.Engine.Core; using Syntriax.Engine.Core.Factory; using Syntriax.Engine.Integration.MonoGame; -using Syntriax.Engine.Network; +using Syntriax.Engine.Systems.Network; using Syntriax.Engine.Physics2D; using Syntriax.Engine.Systems.Tween; diff --git a/Shared/Shared.csproj b/Shared/Shared.csproj index 982010e..62e18bc 100644 --- a/Shared/Shared.csproj +++ b/Shared/Shared.csproj @@ -4,7 +4,6 @@ enable - All @@ -12,5 +11,6 @@ +