diff --git a/Game/Behaviours/PaddleBehaviour.cs b/Game/Behaviours/PaddleBehaviour.cs index d944847..e72e961 100644 --- a/Game/Behaviours/PaddleBehaviour.cs +++ b/Game/Behaviours/PaddleBehaviour.cs @@ -1,13 +1,15 @@ using System; - +using LiteNetLib.Utils; using Microsoft.Xna.Framework.Input; using Syntriax.Engine.Core; using Syntriax.Engine.Input; +using Syntriax.Engine.Network; +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; @@ -26,10 +28,14 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp return; if (isUpPressed) - GameObject.Transform.Position = GameObject.Transform.Position + Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed; + Move(Vector2D.Up); else if (isDownPressed) - GameObject.Transform.Position = GameObject.Transform.Position + -Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed; + Move(-Vector2D.Up); + } + private void Move(Vector2D vectorToMove) + { + GameObject.Transform.Position = GameObject.Transform.Position + vectorToMove * (float)Time.Elapsed.TotalSeconds * Speed; GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low)); } @@ -54,8 +60,35 @@ 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; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed }); } + private void OnUpReleased(IButtonInputs inputs, Keys keys) { isUpPressed = false; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed }); } + private void OnDownPressed(IButtonInputs inputs, Keys keys) { isDownPressed = true; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed }); } + private void OnDownReleased(IButtonInputs inputs, Keys keys) { isDownPressed = false; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed }); } + + public override void ReceiveData(T data) + { + if (data is PaddleInputs paddleInputs) + { + System.Diagnostics.Debug.WriteLine($"Paddle Inputs Arrived: {paddleInputs.IsUpPressed}, {paddleInputs.IsDownPressed}"); + } + } + + [System.Serializable] + public struct PaddleInputs : INetworkPacket + { + public bool IsUpPressed { get; set; } + public bool IsDownPressed { get; set; } + + public void Deserialize(NetDataReader reader) + { + IsUpPressed = reader.GetBool(); + IsDownPressed = reader.GetBool(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(IsUpPressed); + writer.Put(IsDownPressed); + } + } } diff --git a/Game/Network/Abstract/INetworkCommunicator.cs b/Game/Network/Abstract/INetworkCommunicator.cs index 93f996b..fe1fb38 100644 --- a/Game/Network/Abstract/INetworkCommunicator.cs +++ b/Game/Network/Abstract/INetworkCommunicator.cs @@ -4,9 +4,15 @@ namespace Syntriax.Engine.Network.Abstract; public interface INetworkCommunicator { + event OnPacketReceivedDelegate? OnPacketReceived; + EventBasedNetListener Listener { get; } NetManager Manager { get; } void PollEvents(); void Stop(); + + void Send(NetworkPacket Data); + + delegate void OnPacketReceivedDelegate(INetworkCommunicator sender, object packet); } diff --git a/Game/Network/Abstract/INetworkEntity.cs b/Game/Network/Abstract/INetworkEntity.cs index 0a1a67f..e88a1f2 100644 --- a/Game/Network/Abstract/INetworkEntity.cs +++ b/Game/Network/Abstract/INetworkEntity.cs @@ -6,5 +6,7 @@ public interface INetworkEntity uint NetworkId { get; set; } + void ReceiveData(T data); + delegate void OnNetworkIdChangedDelegate(INetworkEntity sender, uint previousId); } diff --git a/Game/Network/Abstract/INetworkPacket.cs b/Game/Network/Abstract/INetworkPacket.cs new file mode 100644 index 0000000..704c76f --- /dev/null +++ b/Game/Network/Abstract/INetworkPacket.cs @@ -0,0 +1,5 @@ +using LiteNetLib.Utils; + +namespace Syntriax.Engine.Network.Abstract; + +public interface INetworkPacket : INetSerializable; diff --git a/Game/Network/NetworkBase.cs b/Game/Network/NetworkBase.cs index 54cdbff..a8c19ca 100644 --- a/Game/Network/NetworkBase.cs +++ b/Game/Network/NetworkBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics; +using System.Linq; +using System.Reflection; using LiteNetLib; using LiteNetLib.Utils; @@ -11,11 +12,13 @@ namespace Syntriax.Engine.Network; public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator { - private readonly NetPacketProcessor netPacketProcessor = new(); + public event INetworkCommunicator.OnPacketReceivedDelegate? OnPacketReceived = null; - private readonly Dictionary networkEntities = []; + protected readonly NetPacketProcessor netPacketProcessor = new(); - private BehaviourCollector networkEntityCollector = null!; + protected readonly Dictionary networkEntities = []; + + protected BehaviourCollector networkEntityCollector = null!; public EventBasedNetListener Listener { get; private set; } = null!; public NetManager Manager { get; private set; } = null!; @@ -28,12 +31,80 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator Manager = new NetManager(Listener); Listener.NetworkReceiveEvent += NetworkReceiveEvent; - netPacketProcessor.SubscribeReusable, NetPeer>(OnPacketArrived); + + netPacketProcessor.RegisterNestedType(); + RegisterPackets(); } - private void OnPacketArrived(NetworkPacket packet, NetPeer peer) + public void RegisterPackets() { - Debug.WriteLine($"Packet Arrived for {packet.NetworkId}: {packet.Data}"); + var packetTypes = Assembly.GetExecutingAssembly().GetTypes() + .Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface) + .ToList(); + + // MethodInfo subscribeMethod = netPacketProcessor.GetType() + // .GetMethod(nameof(NetPacketProcessor.SubscribeReusable), [typeof(Action<,>)]); + + MethodInfo[] subscribeMethods = netPacketProcessor.GetType() + .GetMethods() + .Where(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable)) + .ToArray(); + + MethodInfo subscribeMethod = subscribeMethods + .FirstOrDefault(m => m.GetParameters().Length == 1 && + m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>)); + + MethodInfo[] methodInfos = typeof(NetworkBase) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo method = methodInfos + .FirstOrDefault(m => m.Name == "OnPacketArrived" && m.IsGenericMethod); + + // .GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new Exception(); + + foreach (var packetType in packetTypes) + { + var networkPacketType = typeof(NetworkPacket<>).MakeGenericType(packetType); + MethodInfo genericSubscribe = subscribeMethod.MakeGenericMethod(networkPacketType, typeof(NetPeer)); + + Action handler = (packet, peer) => + { + method = method.MakeGenericMethod(packetType); + method.Invoke(this, [packet, peer]); + }; + genericSubscribe.Invoke(netPacketProcessor, [handler]); + } + } + // private void RegisterPackets() + // { + // IEnumerable packetTypes = Assembly.GetExecutingAssembly().GetTypes().Where( + // t => t.GetInterfaces().Contains(typeof(INetworkPacket)) + // ); + + // MethodInfo subscribeMethod = netPacketProcessor.GetType() + // .GetMethod(nameof(NetPacketProcessor.SubscribeReusable), [typeof(Action<,>)]) ?? throw new Exception(); + + // foreach (var packetType in packetTypes) + // { + // MethodInfo genericSubscribe = subscribeMethod.MakeGenericMethod(packetType, typeof(NetPeer)); + // Action handler = (packet, peer) => + // { + // MethodInfo method = GetType() + // .GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new Exception(); + // method = method.MakeGenericMethod(packetType.GetGenericArguments()[0]); + // method.Invoke(this, [packet, peer]); + // }; + // genericSubscribe.Invoke(netPacketProcessor, [handler]); + // } + // } + + private void OnPacketArrived(NetworkPacket packet, NetPeer peer) where T : INetworkPacket//OnPacketArrived(NetworkPacket packet, NetPeer peer) + { + // Handle packet + Console.WriteLine($"Packet of type {typeof(T)} arrived with data: {packet.Data}"); + if (networkEntities.TryGetValue(packet.NetworkId, out INetworkEntity? entity)) + entity.ReceiveData(packet.Data); + + OnPacketReceived?.Invoke(this, packet); } protected override void OnInitialize() @@ -67,4 +138,6 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator public void Stop() => Manager.Stop(); protected override void OnUpdate() => PollEvents(); + + public abstract void Send(NetworkPacket packet); } diff --git a/Game/Network/NetworkBehaviour.cs b/Game/Network/NetworkBehaviour.cs index b055245..7ff1bf5 100644 --- a/Game/Network/NetworkBehaviour.cs +++ b/Game/Network/NetworkBehaviour.cs @@ -1,6 +1,5 @@ using System; -using LiteNetLib; -using LiteNetLib.Utils; + using Syntriax.Engine.Core; using Syntriax.Engine.Network.Abstract; @@ -48,4 +47,9 @@ public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour IsServer = true; } + + public void SendData(T data) + => NetworkCommunicator.Send(new NetworkPacket() { NetworkId = _networkId, Data = data }); + + public abstract void ReceiveData(T data); } diff --git a/Game/Network/NetworkClient.cs b/Game/Network/NetworkClient.cs index 32fb08b..35b0bcf 100644 --- a/Game/Network/NetworkClient.cs +++ b/Game/Network/NetworkClient.cs @@ -1,14 +1,23 @@ -using System.Diagnostics; -using System.Threading.Tasks; +using LiteNetLib.Utils; + using Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network; public class NetworkClient : NetworkBase, INetworkCommunicatorClient { + private readonly NetDataWriter netDataWriter = new(); + public void Connect(string address, int port, string? password = null) { Manager.Start(); Manager.Connect(address, port, password ?? string.Empty); } + + public override void Send(NetworkPacket packet) + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + Manager.FirstPeer.Send(netDataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered); + } } diff --git a/Game/Network/NetworkManager.cs b/Game/Network/NetworkManager.cs index 9382abf..f32dee1 100644 --- a/Game/Network/NetworkManager.cs +++ b/Game/Network/NetworkManager.cs @@ -25,4 +25,6 @@ public class NetworkManager : NetworkBehaviour, INetworkManager private void OnCollected(BehaviourCollector collector, INetworkEntity entity) => entity.NetworkId = networkIdIndex++; + + public override void ReceiveData(T data) { } } diff --git a/Game/Network/NetworkServer.cs b/Game/Network/NetworkServer.cs index d912c14..1f5f17b 100644 --- a/Game/Network/NetworkServer.cs +++ b/Game/Network/NetworkServer.cs @@ -1,5 +1,6 @@ -using System.Diagnostics; -using System.Threading.Tasks; +using System; +using LiteNetLib.Utils; + using Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network; @@ -10,15 +11,23 @@ public class NetworkServer : NetworkBase, INetworkCommunicatorServer public int MaxConnectionCount { get; private set; } = 0; public int Port { get; private set; } = 8888; - public NetworkServer() : base() + private readonly NetDataWriter netDataWriter = new(); + + public NetworkServer() : this(8888, 0) { } + public NetworkServer(int port, int maxConnectionCount) : base() { + MaxConnectionCount = maxConnectionCount; + Port = port; + Listener.ConnectionRequestEvent += request => { - if (Manager.ConnectedPeersCount < MaxConnectionCount) + if (Manager.ConnectedPeersCount < maxConnectionCount) request.AcceptIfKey(Password); else request.Reject(); }; + + OnPacketReceived += ServerOnPacketReceived; } public void Start(int port, int maxConnectionCount, string? password = null) @@ -29,4 +38,14 @@ public class NetworkServer : NetworkBase, INetworkCommunicatorServer Manager.Start(port); } + + public override void Send(NetworkPacket packet) + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + Manager.SendToAll(netDataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered); + } + + private void ServerOnPacketReceived(INetworkCommunicator sender, object packet) + => Send((NetworkPacket)packet); }