From 214c37e63f52488b3429c2ee85798172ef390edd Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sun, 18 May 2025 00:49:14 +0300 Subject: [PATCH] feat: working networked paddles --- Shared/Behaviours/PaddleBehaviour.cs | 52 +++++------ Shared/GamePong.cs | 39 +++----- Shared/Network/Abstract/INetworkEntity.cs | 5 +- Shared/Network/NetworkManager.cs | 109 ---------------------- 4 files changed, 40 insertions(+), 165 deletions(-) delete mode 100644 Shared/Network/NetworkManager.cs diff --git a/Shared/Behaviours/PaddleBehaviour.cs b/Shared/Behaviours/PaddleBehaviour.cs index 6c3a581..d76282f 100644 --- a/Shared/Behaviours/PaddleBehaviour.cs +++ b/Shared/Behaviours/PaddleBehaviour.cs @@ -19,25 +19,22 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp private bool isUpPressed = false; private bool isDownPressed = false; + private bool isMovingUp = false; + private bool isMovingDown = false; + private IButtonInputs inputs = null!; private INetworkCommunicatorClient networkClient = null!; private INetworkCommunicatorServer? networkServer = null; protected override void OnUpdate() { - if (isUpPressed && isDownPressed) + if (isMovingUp && isMovingDown) return; - if (isUpPressed) - { + if (isMovingUp) Transform.Position = Transform.Position + Vector2D.Up * Universe.Time.DeltaTime * Speed; - networkClient.SendToServer(new EntityPaddlePositionPacket(this)); - } - else if (isDownPressed) - { + else if (isMovingDown) Transform.Position = Transform.Position + -Vector2D.Up * Universe.Time.DeltaTime * Speed; - networkClient.SendToServer(new EntityPaddlePositionPacket(this)); - } Transform.Position = new Vector2D(Transform.Position.X, MathF.Max(MathF.Min(Transform.Position.Y, High), Low)); } @@ -46,9 +43,10 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp { inputs = Universe.FindRequiredBehaviour>(); networkClient = Universe.GetRequiredUniverseObject(); - networkServer = Universe.GetRequiredUniverseObject(); + networkServer = Universe.GetUniverseObject(); networkClient.SubscribeToPackets(ReceiveDataClient); - networkServer.SubscribeToPackets(ReceiveDataServer); + networkServer?.SubscribeToPackets(ReceiveDataServer); + inputs.RegisterOnPress(Up, OnUpPressed); inputs.RegisterOnRelease(Up, OnUpReleased); @@ -65,17 +63,21 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp inputs.UnregisterOnRelease(Down, OnDownReleased); } - private void OnUpPressed(IButtonInputs inputs, Keys keys) => isUpPressed = true; - private void OnUpReleased(IButtonInputs inputs, Keys keys) => isUpPressed = false; - private void OnDownPressed(IButtonInputs inputs, Keys keys) => isDownPressed = true; - private void OnDownReleased(IButtonInputs inputs, Keys keys) => isDownPressed = false; + private void OnUpPressed(IButtonInputs inputs, Keys keys) { isUpPressed = true; networkClient.SendToServer(new EntityPaddlePositionPacket(this)); } + private void OnUpReleased(IButtonInputs inputs, Keys keys) { isUpPressed = false; networkClient.SendToServer(new EntityPaddlePositionPacket(this)); } + private void OnDownPressed(IButtonInputs inputs, Keys keys) { isDownPressed = true; networkClient.SendToServer(new EntityPaddlePositionPacket(this)); } + private void OnDownReleased(IButtonInputs inputs, Keys keys) { isDownPressed = false; networkClient.SendToServer(new EntityPaddlePositionPacket(this)); } public void ReceiveDataClient(T data, string _) { if (data is not EntityPaddlePositionPacket position) return; - Transform.Position = position.Position; + if (position.EntityId.CompareTo(Id) != 0) + return; + + isMovingUp = position.IsUpPressed; + isMovingDown = position.IsDownPressed; } public void ReceiveDataServer(T data, string fromClientId) @@ -83,22 +85,17 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp if (data is not EntityPaddlePositionPacket position) return; - Transform.Position = position.Position; - networkServer?.SendToClient("*", position); - } - - public void ReceiveDataClient(T data) - { - if (data is not EntityPaddlePositionPacket position) + if (position.EntityId.CompareTo(Id) != 0) return; - Transform.Position = position.Position; + networkServer?.SendToClient("*", position); } public class EntityPaddlePositionPacket : IEntityNetworkPacket { - public string EntityId = default!; - public Vector2D Position = default!; + public string EntityId { get; set; } = default!; + public bool IsUpPressed { get; set; } = default!; + public bool IsDownPressed { get; set; } = default!; string IEntityNetworkPacket.EntityId => EntityId; @@ -106,7 +103,8 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp public EntityPaddlePositionPacket(PaddleBehaviour paddleBehaviour) { EntityId = paddleBehaviour.Id; - Position = paddleBehaviour.Transform.Position; + IsUpPressed = paddleBehaviour.isUpPressed; + IsDownPressed = paddleBehaviour.isDownPressed; } } } diff --git a/Shared/GamePong.cs b/Shared/GamePong.cs index 69355dd..210bc2a 100644 --- a/Shared/GamePong.cs +++ b/Shared/GamePong.cs @@ -1,16 +1,18 @@ using System; +using System.Linq; + using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; -using Pong.Behaviours; using Apos.Shapes; +using Pong.Behaviours; + using Syntriax.Engine.Core; -using Syntriax.Engine.Physics2D; -using System.Linq; -using Syntriax.Engine.Network; using Syntriax.Engine.Core.Factory; +using Syntriax.Engine.Network; +using Syntriax.Engine.Physics2D; namespace Pong; @@ -76,13 +78,12 @@ public class GamePong : Game universe.Register(platformSpecificUniverseObject); - NetworkManager networkManager = universe.InstantiateUniverseObject().SetUniverseObject("Network Manager"); - networkManager.NetworkCommunicator = universe.InstantiateUniverseObject().SetUniverseObject("Client").Connect("localhost", 8888).SendToServer(new TestMessagePacket("Hola")).SubscribeToPackets(OnClientPacketReceived); if (Environment.GetCommandLineArgs().FirstOrDefault(x => x.CompareTo("-server") == 0) is not null) { - universe.InstantiateUniverseObject().SetUniverseObject("Server").Start(8888, 2).SubscribeToPackets(OnServerPacketReceived); + universe.InstantiateUniverseObject().SetUniverseObject("Server").Start(8888, 2); Window.Title = $"{Window.Title} - Server"; } + universe.InstantiateUniverseObject().SetUniverseObject("Client").Connect("localhost", 8888); universe.InstantiateUniverseObject().SetUniverseObject("Physics Engine 2D"); //////////////////////////////////////////////////////////////////////////////////// @@ -108,19 +109,21 @@ public class GamePong : Game //////////////////////////////////////////////////////////////////////////////////// IUniverseObject leftPaddle = UniverseObjectFactory.Instantiate().SetUniverseObject("Left Paddle"); - leftPaddle.Id = "lp"; leftPaddle.BehaviourController.AddBehaviour().SetTransform(position: new Vector2D(-468f, 0f), scale: new Vector2D(15f, 60f)) - .BehaviourController.AddBehaviour(Keys.W, Keys.S, 228f, -228f, 400f) .BehaviourController.AddBehaviour(Shape2D.Square) .BehaviourController.AddBehaviour().IsStatic = true; + PaddleBehaviour leftPaddleBehaviour = BehaviourFactory.Instantiate(Keys.W, Keys.S, 228f, -228f, 400f); + leftPaddleBehaviour.Id = "lp"; + leftPaddle.BehaviourController.AddBehaviour(leftPaddleBehaviour); universe.Register(leftPaddle); IUniverseObject rightPaddle = UniverseObjectFactory.Instantiate().SetUniverseObject("Right Paddle"); - rightPaddle.Id = "rp"; rightPaddle.BehaviourController.AddBehaviour().SetTransform(position: new Vector2D(468f, 0f), scale: new Vector2D(15f, 60f)) - .BehaviourController.AddBehaviour(Keys.Up, Keys.Down, 228f, -228f, 400f) .BehaviourController.AddBehaviour(Shape2D.Square) .BehaviourController.AddBehaviour().IsStatic = true; + PaddleBehaviour rightPaddleBehaviour = BehaviourFactory.Instantiate(Keys.Up, Keys.Down, 228f, -228f, 400f); + rightPaddleBehaviour.Id = "rp"; + rightPaddle.BehaviourController.AddBehaviour(rightPaddleBehaviour); universe.Register(rightPaddle); //////////////////////////////////////////////////////////////////////////////////// @@ -158,20 +161,6 @@ public class GamePong : Game .BehaviourController.AddBehaviour(false, spriteFont); } - private void OnClientPacketReceived(TestMessagePacket packet, string from) - { - Console.WriteLine($"Message received from server: {packet.Message}"); - leftText.Text = packet.Message; - } - - private void OnServerPacketReceived(TestMessagePacket packet, string from) - { - Console.WriteLine($"Message received from client: {packet.Message}"); - rightText.Text = packet.Message; - - universe.GetRequiredUniverseObject().SendToClient(from, new TestMessagePacket("Reply!")); - } - protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) diff --git a/Shared/Network/Abstract/INetworkEntity.cs b/Shared/Network/Abstract/INetworkEntity.cs index ad458b2..6d874e9 100644 --- a/Shared/Network/Abstract/INetworkEntity.cs +++ b/Shared/Network/Abstract/INetworkEntity.cs @@ -2,7 +2,4 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Network; -public interface INetworkEntity : IEntity -{ - void ReceiveDataClient(T data); -} +public interface INetworkEntity : IEntity; diff --git a/Shared/Network/NetworkManager.cs b/Shared/Network/NetworkManager.cs deleted file mode 100644 index a7ed7f2..0000000 --- a/Shared/Network/NetworkManager.cs +++ /dev/null @@ -1,109 +0,0 @@ -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 _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) - { - if (entityDataPacket is IEntityNetworkPacket entityNetworkPacket) - _networkEntities[entityNetworkPacket.EntityId].ReceiveDataClient(entityDataPacket); - } - - private void OnCollected(IBehaviourCollector sender, INetworkEntity behaviourCollected) - { - if (!_networkEntities.TryAdd(behaviourCollected.Id, behaviourCollected)) - throw new($"Unable to add {behaviourCollected.Id} to {nameof(NetworkManager)}"); - } - - private void OnRemoved(IBehaviourCollector sender, INetworkEntity behaviourRemoved) - { - _networkEntities.Remove(behaviourRemoved.Id); - } - - protected override void OnEnteringUniverse(IUniverse universe) => _networkEntityCollector.Assign(universe); - protected override void OnExitingUniverse(IUniverse universe) => _networkEntityCollector.Unassign(); -}