From 3c1528d8792b04dd743568f76a529e1ceaddcaa2 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Mon, 12 Feb 2024 16:35:24 +0300 Subject: [PATCH] wip: Network --- Game/Behaviours/BallBehaviour.cs | 19 +++-- Game/Behaviours/PaddleBehaviour.cs | 18 +++-- Game/Behaviours/PongManagerBehaviour.cs | 23 +++--- Game/GamePong.cs | 42 ++++++----- Game/Network/Abstract/INetworkBehaviour.cs | 3 + Game/Network/Abstract/INetworkCommunicator.cs | 3 +- Game/Network/Abstract/INetworkEntity.cs | 3 + Game/Network/Abstract/INetworkManager.cs | 10 +-- Game/Network/NetworkBase.cs | 13 +++- Game/Network/NetworkBehaviour.cs | 74 +++++++++++++++++-- Game/Network/NetworkManager.cs | 31 ++++---- 11 files changed, 159 insertions(+), 80 deletions(-) diff --git a/Game/Behaviours/BallBehaviour.cs b/Game/Behaviours/BallBehaviour.cs index e795f47..020d60f 100644 --- a/Game/Behaviours/BallBehaviour.cs +++ b/Game/Behaviours/BallBehaviour.cs @@ -13,7 +13,7 @@ using Syntriax.Engine.Physics2D.Abstract; namespace Pong.Behaviours; -public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader +public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader { public Vector2D StartDirection { get; private set; } = Vector2D.Zero; public float Speed { get; set; } = 500f; @@ -42,7 +42,7 @@ public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader rigidBody = foundRigidBody; communicator = foundCommunicator; - communicator.RegisterEntityListener(this, OnDataReceived); + communicator.Listener.PeerConnectedEvent += PeerConnected; if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager)) { pongManager.OnReset += ResetBall; @@ -51,10 +51,11 @@ public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader } } - private void OnDataReceived(NetPacketReader reader) + private void PeerConnected(NetPeer peer) { - rigidBody.Transform.Position = reader.GetVector2D(); - rigidBody.Velocity = reader.GetVector2D(); + if (!IsServer) + return; + if (communicator.Manager.ConnectedPeersCount == 1) } private void DisableBall(PongManagerBehaviour pongManager) @@ -106,12 +107,18 @@ public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader if (communicator is not INetworkServer server) return; - LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetEntityWriter(this); + LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this); dataWriter.Put(rigidBody.Transform.Position); dataWriter.Put(rigidBody.Velocity); server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered); } + protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) + { + rigidBody.Transform.Position = reader.GetVector2D(); + rigidBody.Velocity = reader.GetVector2D(); + } + public BallBehaviour() { } public BallBehaviour(Vector2D startDirection, float speed) { diff --git a/Game/Behaviours/PaddleBehaviour.cs b/Game/Behaviours/PaddleBehaviour.cs index 1856c65..82e732a 100644 --- a/Game/Behaviours/PaddleBehaviour.cs +++ b/Game/Behaviours/PaddleBehaviour.cs @@ -12,7 +12,7 @@ using Syntriax.Engine.Network.Abstract; namespace Pong.Behaviours; -public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : BehaviourOverride +public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : NetworkBehaviour { private Keys Up { get; } = Up; private Keys Down { get; } = Down; @@ -41,9 +41,12 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp private void MovePaddle(Vector2D vectorToAdd) { + if (!LocalAssigned && !IsServer) + return; + GameObject.Transform.Position += vectorToAdd; - NetDataWriter dataWriter = communicator.GetEntityWriter(this); + NetDataWriter dataWriter = communicator.GetMessageWriter(this); dataWriter.Put(Transform.Position); communicator.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.Unreliable); } @@ -63,12 +66,6 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp inputs.RegisterOnRelease(Down, OnDownReleased); communicator = foundCommunicator; - communicator.RegisterEntityListener(this, OnDataReceived); - } - - private void OnDataReceived(NetPacketReader reader) - { - Transform.Position = reader.GetVector2D(); } protected override void OnFinalize() @@ -84,4 +81,9 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp 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; + + protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) + { + Transform.Position = reader.GetVector2D(); + } } diff --git a/Game/Behaviours/PongManagerBehaviour.cs b/Game/Behaviours/PongManagerBehaviour.cs index fbe3693..5441b38 100644 --- a/Game/Behaviours/PongManagerBehaviour.cs +++ b/Game/Behaviours/PongManagerBehaviour.cs @@ -10,7 +10,7 @@ using Syntriax.Engine.Network.Abstract; namespace Pong.Behaviours; -public class PongManagerBehaviour : BehaviourOverride +public class PongManagerBehaviour : NetworkBehaviour { public Action? OnReset { get; set; } = null; public Action? OnFinished { get; set; } = null; @@ -41,16 +41,6 @@ public class PongManagerBehaviour : BehaviourOverride buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => Reset()); communicator = foundCommunicator; - communicator.RegisterEntityListener(this, OnMessageReceived); - } - - private void OnMessageReceived(NetPacketReader reader) - { - ScoreLeft = reader.GetInt(); - ScoreRight = reader.GetInt(); - OnScoresUpdated?.Invoke(this); - - CheckFinish(); } public void ScoreToLeft() @@ -97,9 +87,18 @@ public class PongManagerBehaviour : BehaviourOverride if (communicator is not INetworkServer server) return; - LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetEntityWriter(this); + LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this); dataWriter.Put(ScoreLeft); dataWriter.Put(ScoreRight); server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered); } + + protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) + { + ScoreLeft = reader.GetInt(); + ScoreRight = reader.GetInt(); + OnScoresUpdated?.Invoke(this); + + CheckFinish(); + } } diff --git a/Game/GamePong.cs b/Game/GamePong.cs index 2f93f3f..9110676 100644 --- a/Game/GamePong.cs +++ b/Game/GamePong.cs @@ -71,6 +71,28 @@ public class GamePong : Game //////////////////////////////////////////////////////////////////////////////////// + string[] commandLineArguments = Environment.GetCommandLineArgs(); + if (commandLineArguments.Length != 1) + { + if (commandLineArguments[1].Equals("server", StringComparison.OrdinalIgnoreCase)) + { + gameManager.InstantiateGameObject().BehaviourController.AddBehaviour().Start(8888, 2); + Window.Title = "Pong - Server"; + } + else + { + Window.Title = $"Pong - Client -> {commandLineArguments[1]}"; + gameManager.InstantiateGameObject().BehaviourController.AddBehaviour().Connect(commandLineArguments[1], 8888); + } + } + else + { + gameManager.InstantiateGameObject().BehaviourController.AddBehaviour().Connect("127.0.0.1", 8888); + Window.Title = $"Pong - Client -> 127.0.0.1"; + } + + //////////////////////////////////////////////////////////////////////////////////// + IGameObject gameObjectCamera = gameManager.InstantiateGameObject().SetGameObject("Camera"); ; gameObjectCamera.BehaviourController.AddBehaviour(); cameraBehaviour = gameObjectCamera.BehaviourController.AddBehaviour(graphics); @@ -138,26 +160,6 @@ public class GamePong : Game IGameObject gameObjectRightScoreText = gameManager.InstantiateGameObject().SetGameObject("Score Right"); gameObjectRightScoreText.Transform.SetTransform(position: new Vector2D(250f, 250f), scale: Vector2D.One * .25f); gameObjectRightScoreText.BehaviourController.AddBehaviour(false, spriteFont); - - string[] commandLineArguments = Environment.GetCommandLineArgs(); - if (commandLineArguments.Length != 1) - { - if (commandLineArguments[1].Equals("server", StringComparison.OrdinalIgnoreCase)) - { - pongManager.BehaviourController.AddBehaviour().Start(8888, 2); - Window.Title = "Pong - Server"; - } - else - { - Window.Title = $"Pong - Client -> {commandLineArguments[1]}"; - pongManager.BehaviourController.AddBehaviour().Connect(commandLineArguments[1], 8888); - } - } - else - { - pongManager.BehaviourController.AddBehaviour().Connect("127.0.0.1", 8888); - Window.Title = $"Pong - Client -> 127.0.0.1"; - } } protected override void Update(GameTime gameTime) diff --git a/Game/Network/Abstract/INetworkBehaviour.cs b/Game/Network/Abstract/INetworkBehaviour.cs index 2bcadd0..b1afa54 100644 --- a/Game/Network/Abstract/INetworkBehaviour.cs +++ b/Game/Network/Abstract/INetworkBehaviour.cs @@ -12,4 +12,7 @@ public interface INetworkBehaviour : IBehaviour bool IsClient { get; } INetworkCommunicator NetworkCommunicator { get; } + + public void RequestAssignment(); + public void ReleaseAssignment(); } diff --git a/Game/Network/Abstract/INetworkCommunicator.cs b/Game/Network/Abstract/INetworkCommunicator.cs index ad9d788..4ee6a17 100644 --- a/Game/Network/Abstract/INetworkCommunicator.cs +++ b/Game/Network/Abstract/INetworkCommunicator.cs @@ -15,7 +15,8 @@ public interface INetworkCommunicator void PollEvents(); void Stop(); - void RegisterEntityListener(IEntity entity, Action onDataReceived); + void RegisterEntityListener(IEntity entity, Action onDataReceived); void UnregisterEntityListener(IEntity entity); NetDataWriter GetEntityWriter(IEntity entity); + NetDataWriter GetMessageWriter(IEntity entity); } diff --git a/Game/Network/Abstract/INetworkEntity.cs b/Game/Network/Abstract/INetworkEntity.cs index 05aa4f5..85ee4ec 100644 --- a/Game/Network/Abstract/INetworkEntity.cs +++ b/Game/Network/Abstract/INetworkEntity.cs @@ -1,7 +1,10 @@ +using System; + namespace Syntriax.Engine.Network.Abstract; internal interface INetworkEntity { + Action OnNetworkIdChanged { get; set; } int NetworkId { get; set; } void SetNetworkId(int id); diff --git a/Game/Network/Abstract/INetworkManager.cs b/Game/Network/Abstract/INetworkManager.cs index a5320b2..b42dc65 100644 --- a/Game/Network/Abstract/INetworkManager.cs +++ b/Game/Network/Abstract/INetworkManager.cs @@ -1,14 +1,10 @@ -using System; -using System.Threading.Tasks; -using Syntriax.Engine.Core.Abstract; - namespace Syntriax.Engine.Network.Abstract; public interface INetworkManager { - Action? OnNetworkGameObjectInstantiated { get; set; } + // Action? OnNetworkGameObjectInstantiated { get; set; } - INetworkCommunicator Communicator { get; } + INetworkCommunicator NetworkCommunicator { get; } - Task Instantiate(params object?[]? args) where T : class, IGameObject; + // Task Instantiate(params object?[]? args) where T : class, IGameObject; } diff --git a/Game/Network/NetworkBase.cs b/Game/Network/NetworkBase.cs index 49a62d3..a70d929 100644 --- a/Game/Network/NetworkBase.cs +++ b/Game/Network/NetworkBase.cs @@ -31,17 +31,17 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator protected override void OnUpdate() => PollEvents(); protected override void OnFinalize() => Stop(); - private Dictionary> callbacks = new(32); + private readonly Dictionary> callbacks = new(32); private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) { if (callbacks.TryGetValue(reader.GetString(), out var action)) - action?.Invoke(reader); + action?.Invoke(reader, peer); reader.Recycle(); } - public void RegisterEntityListener(IEntity entity, Action onDataReceived) + public void RegisterEntityListener(IEntity entity, Action onDataReceived) { if (callbacks.ContainsKey(entity.Id)) return; @@ -58,6 +58,13 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator entity.OnIdChanged -= OnEntityIdChanged; } + public NetDataWriter GetMessageWriter(IEntity entity) + { + NetDataWriter netDataWriter = GetEntityWriter(entity); + netDataWriter.Put((int)MessageType.Message); + return netDataWriter; + } + public NetDataWriter GetEntityWriter(IEntity entity) { NetDataWriter netDataWriter = new(); diff --git a/Game/Network/NetworkBehaviour.cs b/Game/Network/NetworkBehaviour.cs index 4ba8658..63de922 100644 --- a/Game/Network/NetworkBehaviour.cs +++ b/Game/Network/NetworkBehaviour.cs @@ -1,9 +1,13 @@ +using System; + +using LiteNetLib; + using Syntriax.Engine.Core; using Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network; -public class NetworkBehaviour : BehaviourOverride, INetworkBehaviour, INetworkEntity +public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour { public int NetworkId { get; private set; } = 0; @@ -18,7 +22,9 @@ public class NetworkBehaviour : BehaviourOverride, INetworkBehaviour, INetworkEn { NetworkCommunicator = BehaviourController.GetBehaviourInParent() ?? GameObject.GameManager.FindBehaviour() - ?? throw new System.Exception($"Could not find an {nameof(INetworkCommunicator)}."); + ?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}."); + + NetworkCommunicator.RegisterEntityListener(this, OnMessageReceivedInternal); if (NetworkCommunicator is INetworkClient client) { @@ -28,10 +34,66 @@ public class NetworkBehaviour : BehaviourOverride, INetworkBehaviour, INetworkEn IsServer = true; LocalAssigned = true; - - // TODO Set and send Network Id } - int INetworkEntity.NetworkId { get => NetworkId; set => NetworkId = value; } - void INetworkEntity.SetNetworkId(int id) => NetworkId = id; + public void ReleaseAssignment() + { + if (IsServer) + return; + + var netDataWriter = NetworkCommunicator.GetEntityWriter(this); + netDataWriter.Put((int)MessageType.AssignmentRelease); + NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); + } + + public void RequestAssignment() + { + if (IsServer) + return; + + var netDataWriter = NetworkCommunicator.GetEntityWriter(this); + netDataWriter.Put((int)MessageType.AssignmentRequest); + NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); + } + + protected abstract void OnMessageReceived(NetPacketReader reader, NetPeer peer); + private void OnMessageReceivedInternal(NetPacketReader reader, NetPeer peer) + { + MessageType messageType = (MessageType)reader.GetInt(); + + if (IsServer) + { + switch (messageType) + { + case MessageType.Message: OnMessageReceived(reader, peer); break; + case MessageType.AssignmentRequest: + case MessageType.AssignmentRelease: + var netDataWriter = NetworkCommunicator.GetEntityWriter(this); + netDataWriter.Put((int)(messageType == MessageType.AssignmentRequest ? MessageType.Assigned : MessageType.Unassigned)); + peer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); + break; + default: + break; + } + return; + } + + switch (messageType) + { + case MessageType.Message: OnMessageReceived(reader, peer); break; + case MessageType.Assigned: LocalAssigned = true; break; + case MessageType.Unassigned: LocalAssigned = false; break; + default: + break; + } + } +} + +internal enum MessageType +{ + Message, + AssignmentRequest, + AssignmentRelease, + Assigned, + Unassigned, } diff --git a/Game/Network/NetworkManager.cs b/Game/Network/NetworkManager.cs index e493d2c..978c21e 100644 --- a/Game/Network/NetworkManager.cs +++ b/Game/Network/NetworkManager.cs @@ -1,34 +1,31 @@ -using System; -using System.Threading.Tasks; +using LiteNetLib; using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Abstract; + using Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network; public class NetworkManager : NetworkBehaviour, INetworkManager { - public Action? OnNetworkGameObjectInstantiated { get; set; } = null; - - public INetworkCommunicator Communicator { get; private set; } = null!; private BehaviourCollector entities = null!; + private static int networkIdIndex = 0; + protected override void OnInitialize() { base.OnInitialize(); + + ((INetworkEntity)this).SetNetworkId(networkIdIndex++); + entities = new(GameObject.GameManager); + foreach (var entity in entities) + entity.SetNetworkId(networkIdIndex++); + + entities.OnCollected += OnCollected; } - public Task Instantiate(params object?[]? args) where T : class, IGameObject - { - return Task.Run(() => - { - if (IsServer) - return GameObject.GameManager.InstantiateGameObject(args); + private void OnCollected(BehaviourCollector collector, INetworkEntity entity) + => entity.SetNetworkId(networkIdIndex++); - // Request Instantiation From Server - int id = 0; - return GameObject.GameManager.InstantiateGameObject(args); - }); - } + protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) { } }