From 91d301677fb2db8ab3a4fd34daafcd14661ca5b8 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Mon, 15 Jul 2024 17:00:04 +0300 Subject: [PATCH] Test --- Game/Game.csproj | 1 + Game/Network/Abstract/INetworkBehaviour.cs | 11 ++ Game/Network/Abstract/INetworkCommunicator.cs | 19 +++ .../Abstract/INetworkCommunicatorClient.cs | 6 + .../Abstract/INetworkCommunicatorServer.cs | 10 ++ Game/Network/Abstract/INetworkEntity.cs | 17 +++ Game/Network/Abstract/INetworkManager.cs | 10 ++ Game/Network/Abstract/NetworkPacket.cs | 21 ++++ Game/Network/NetworkBase.cs | 109 ++++++++++++++++++ Game/Network/NetworkBehaviour.cs | 68 +++++++++++ Game/Network/NetworkClient.cs | 12 ++ Game/Network/NetworkExtensions.cs | 21 ++++ Game/Network/NetworkManager.cs | 28 +++++ Game/Network/NetworkServer.cs | 30 +++++ 14 files changed, 363 insertions(+) create mode 100644 Game/Network/Abstract/INetworkBehaviour.cs create mode 100644 Game/Network/Abstract/INetworkCommunicator.cs create mode 100644 Game/Network/Abstract/INetworkCommunicatorClient.cs create mode 100644 Game/Network/Abstract/INetworkCommunicatorServer.cs create mode 100644 Game/Network/Abstract/INetworkEntity.cs create mode 100644 Game/Network/Abstract/INetworkManager.cs create mode 100644 Game/Network/Abstract/NetworkPacket.cs create mode 100644 Game/Network/NetworkBase.cs create mode 100644 Game/Network/NetworkBehaviour.cs create mode 100644 Game/Network/NetworkClient.cs create mode 100644 Game/Network/NetworkExtensions.cs create mode 100644 Game/Network/NetworkManager.cs create mode 100644 Game/Network/NetworkServer.cs diff --git a/Game/Game.csproj b/Game/Game.csproj index f15750f..6136642 100644 --- a/Game/Game.csproj +++ b/Game/Game.csproj @@ -21,6 +21,7 @@ + diff --git a/Game/Network/Abstract/INetworkBehaviour.cs b/Game/Network/Abstract/INetworkBehaviour.cs new file mode 100644 index 0000000..1f18e94 --- /dev/null +++ b/Game/Network/Abstract/INetworkBehaviour.cs @@ -0,0 +1,11 @@ +using Syntriax.Engine.Core.Abstract; + +namespace Syntriax.Engine.Network.Abstract; + +public interface INetworkBehaviour : IBehaviour, INetworkEntity +{ + bool IsServer { get; } + bool IsClient { get; } + + INetworkCommunicator NetworkCommunicator { get; } +} diff --git a/Game/Network/Abstract/INetworkCommunicator.cs b/Game/Network/Abstract/INetworkCommunicator.cs new file mode 100644 index 0000000..33e1819 --- /dev/null +++ b/Game/Network/Abstract/INetworkCommunicator.cs @@ -0,0 +1,19 @@ +using LiteNetLib; +using LiteNetLib.Utils; + +namespace Syntriax.Engine.Network.Abstract; + +public interface INetworkCommunicator +{ + EventBasedNetListener Listener { get; } + NetManager Manager { get; } + + void PollEvents(); + void Stop(); + + void RegisterEntityPacketListener(INetworkEntity networkEntity, EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable; + void UnregisterEntityPacketListener(INetworkEntity networkEntity, EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable; + void SendEntityPacket(INetworkEntity networkEntity, T packet, params NetPeer[] netPeer) where T : INetSerializable; + + delegate void EntityPacketReceivedDelegate(INetworkEntity networkEntity, object packet, NetPeer netPeer); +} diff --git a/Game/Network/Abstract/INetworkCommunicatorClient.cs b/Game/Network/Abstract/INetworkCommunicatorClient.cs new file mode 100644 index 0000000..a195c5e --- /dev/null +++ b/Game/Network/Abstract/INetworkCommunicatorClient.cs @@ -0,0 +1,6 @@ +namespace Syntriax.Engine.Network.Abstract; + +public interface INetworkCommunicatorClient : INetworkCommunicator +{ + void Connect(string address, int port, string? password = null); +} diff --git a/Game/Network/Abstract/INetworkCommunicatorServer.cs b/Game/Network/Abstract/INetworkCommunicatorServer.cs new file mode 100644 index 0000000..4364238 --- /dev/null +++ b/Game/Network/Abstract/INetworkCommunicatorServer.cs @@ -0,0 +1,10 @@ +namespace Syntriax.Engine.Network.Abstract; + +public interface INetworkCommunicatorServer : INetworkCommunicator +{ + string Password { get; } + int MaxConnectionCount { get; } + int Port { get; } + + void Start(int port, int maxConnectionCount, string? password = null); +} diff --git a/Game/Network/Abstract/INetworkEntity.cs b/Game/Network/Abstract/INetworkEntity.cs new file mode 100644 index 0000000..45c8c81 --- /dev/null +++ b/Game/Network/Abstract/INetworkEntity.cs @@ -0,0 +1,17 @@ +using LiteNetLib.Utils; + +namespace Syntriax.Engine.Network.Abstract; + +public interface INetworkEntity +{ + event OnNetworkIdChangedDelegate? OnNetworkIdChanged; + + uint NetworkId { get; set; } + + delegate void OnNetworkIdChangedDelegate(INetworkEntity sender, uint previousId); + delegate void PacketReceivedDelegate(INetworkEntity entity, object packet); + + void RegisterPacketListener(PacketReceivedDelegate onPacketReceived) where T : INetSerializable; + void UnregisterPacketListener(PacketReceivedDelegate onPacketReceived) where T : INetSerializable; + void SendPacket(T packet) where T : INetSerializable; +} diff --git a/Game/Network/Abstract/INetworkManager.cs b/Game/Network/Abstract/INetworkManager.cs new file mode 100644 index 0000000..b42dc65 --- /dev/null +++ b/Game/Network/Abstract/INetworkManager.cs @@ -0,0 +1,10 @@ +namespace Syntriax.Engine.Network.Abstract; + +public interface INetworkManager +{ + // Action? OnNetworkGameObjectInstantiated { get; set; } + + INetworkCommunicator NetworkCommunicator { get; } + + // Task Instantiate(params object?[]? args) where T : class, IGameObject; +} diff --git a/Game/Network/Abstract/NetworkPacket.cs b/Game/Network/Abstract/NetworkPacket.cs new file mode 100644 index 0000000..e1d9bc2 --- /dev/null +++ b/Game/Network/Abstract/NetworkPacket.cs @@ -0,0 +1,21 @@ +using LiteNetLib.Utils; + +namespace Syntriax.Engine.Network.Abstract; + +public class NetworkPacket() + : INetSerializable + where T : INetSerializable +{ + public uint NetworkId = 0; + public T Data = default!; + + public void Deserialize(NetDataReader reader) + { + NetworkId = reader.GetUInt(); + } + + public void Serialize(NetDataWriter writer) + { + writer.Put(NetworkId); + } +} diff --git a/Game/Network/NetworkBase.cs b/Game/Network/NetworkBase.cs new file mode 100644 index 0000000..aa9b028 --- /dev/null +++ b/Game/Network/NetworkBase.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using LiteNetLib; +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Network.Abstract; + +namespace Syntriax.Engine.Network; + +public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator +{ + private readonly NetPacketProcessor netPacketProcessor = new(); + + private readonly Dictionary networkEntities = []; + private readonly Dictionary> callbacks = []; + + private BehaviourCollector networkEntityCollector = null!; + + public EventBasedNetListener Listener { get; private set; } = null!; + public NetManager Manager { get; private set; } = null!; + + public NetworkBase() + { + Priority = 10; + + Listener = new EventBasedNetListener(); + Manager = new NetManager(Listener); + + Listener.NetworkReceiveEvent += NetworkReceiveEvent; + } + + protected override void OnInitialize() + { + base.OnInitialize(); + + networkEntityCollector = new(GameObject.GameManager); + networkEntityCollector.OnCollected += OnNetworkEntityCollected; + networkEntityCollector.OnRemoved += OnNetworkEntityRemoved; + } + protected override void OnFinalize() + { + networkEntityCollector.OnCollected -= OnNetworkEntityCollected; + networkEntityCollector.OnRemoved -= OnNetworkEntityRemoved; + Stop(); + } + + private void OnNetworkEntityCollected(BehaviourCollector sender, INetworkEntity behaviourCollected) + => networkEntities.Add(behaviourCollected.NetworkId, behaviourCollected); + + private void OnNetworkEntityRemoved(BehaviourCollector sender, INetworkEntity behaviourRemoved) + => networkEntities.Remove(behaviourRemoved.NetworkId); + + private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) + { + netPacketProcessor.ReadAllPackets(reader, peer); + } + + public void PollEvents() => Manager.PollEvents(); + public void Stop() => Manager.Stop(); + + protected override void OnUpdate() => PollEvents(); + + public void RegisterEntityPacketListener(INetworkEntity networkEntity, INetworkCommunicator.EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable + { + if (!callbacks.TryGetValue(typeof(T), out var list)) + { + list = []; + callbacks.Add(typeof(T), list); + netPacketProcessor.SubscribeReusable, NetPeer>(OnPacketReceived); + } + + if (list.Contains(onPacketReceived)) + return; + + list.Add(onPacketReceived); + } + + public void UnregisterEntityPacketListener(INetworkEntity networkEntity, INetworkCommunicator.EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable + { + if (!callbacks.TryGetValue(typeof(T), out var list)) + return; + + list.Remove(onPacketReceived); + } + + public void SendEntityPacket(INetworkEntity networkEntity, T packet, params NetPeer[] netPeers) where T : INetSerializable + { + NetworkPacket networkPacket = new() { NetworkId = networkEntity.NetworkId, Data = packet }; + NetDataWriter netDataWriter = new(); + netPacketProcessor.Write(netDataWriter, networkPacket); + foreach (var netPeer in netPeers) + netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); + } + + private void OnPacketReceived(NetworkPacket packet, NetPeer peer) where T : INetSerializable + { + Debug.WriteLine($"Packet Received: {packet.NetworkId} - {typeof(T)}"); + + if (!callbacks.TryGetValue(typeof(T), out var list)) + return; + + INetworkEntity networkEntity = networkEntities[packet.NetworkId]; + foreach (INetworkCommunicator.EntityPacketReceivedDelegate callback in list) + callback.Invoke(networkEntity, packet, peer); + } +} diff --git a/Game/Network/NetworkBehaviour.cs b/Game/Network/NetworkBehaviour.cs new file mode 100644 index 0000000..832b3de --- /dev/null +++ b/Game/Network/NetworkBehaviour.cs @@ -0,0 +1,68 @@ +using System; +using LiteNetLib; +using LiteNetLib.Utils; +using Syntriax.Engine.Core; +using Syntriax.Engine.Network.Abstract; + +namespace Syntriax.Engine.Network; + +public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour +{ + public event INetworkEntity.OnNetworkIdChangedDelegate? OnNetworkIdChanged = null; + + private uint _networkId = 0; + public uint NetworkId + { + get => _networkId; + set + { + if (!IsServer) + return; + + if (value == _networkId) + return; + + uint previousNetworkId = _networkId; + _networkId = value; + OnNetworkIdChanged?.Invoke(this, previousNetworkId); + } + } + + public bool IsServer { get; private set; } = false; + public bool IsClient { get; private set; } = false; + + public INetworkCommunicator NetworkCommunicator { get; private set; } = null!; + + + protected override void OnInitialize() + { + NetworkCommunicator = BehaviourController.GetBehaviourInParent() + ?? GameObject.GameManager.FindBehaviour() + ?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}."); + + if (NetworkCommunicator is INetworkCommunicatorClient client) + { + IsClient = true; + return; + } + + IsServer = true; + } + + public void RegisterPacketListener(INetworkEntity.PacketReceivedDelegate onPacketReceived) where T : INetSerializable + + private void OnEntityPacketReceived(INetworkEntity networkEntity, object packet, NetPeer netPeer) + { + throw new NotImplementedException(); + } + + public void UnregisterPacketListener(INetworkEntity.PacketReceivedDelegate onPacketReceived) where T : INetSerializable + { + throw new NotImplementedException(); + } + + public void SendPacket(T packet) where T : INetSerializable + { + throw new NotImplementedException(); + } +} diff --git a/Game/Network/NetworkClient.cs b/Game/Network/NetworkClient.cs new file mode 100644 index 0000000..24ef10f --- /dev/null +++ b/Game/Network/NetworkClient.cs @@ -0,0 +1,12 @@ +using Syntriax.Engine.Network.Abstract; + +namespace Syntriax.Engine.Network; + +public class NetworkClient : NetworkBase, INetworkCommunicatorClient +{ + public void Connect(string address, int port, string? password = null) + { + Manager.Start(); + Manager.Connect(address, port, password ?? string.Empty); + } +} diff --git a/Game/Network/NetworkExtensions.cs b/Game/Network/NetworkExtensions.cs new file mode 100644 index 0000000..e98e479 --- /dev/null +++ b/Game/Network/NetworkExtensions.cs @@ -0,0 +1,21 @@ +using LiteNetLib; +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Network; + +public static class NetworkExtensions +{ + public static Vector2D GetVector2D(this NetPacketReader reader) + => new(reader.GetFloat(), reader.GetFloat()); + + public static void GetVector2D(this NetPacketReader reader, out Vector2D vector2D) + => vector2D = new(reader.GetFloat(), reader.GetFloat()); + + public static void Put(this NetDataWriter writer, Vector2D vector) + { + writer.Put(vector.X); + writer.Put(vector.Y); + } +} diff --git a/Game/Network/NetworkManager.cs b/Game/Network/NetworkManager.cs new file mode 100644 index 0000000..9382abf --- /dev/null +++ b/Game/Network/NetworkManager.cs @@ -0,0 +1,28 @@ +using Syntriax.Engine.Core; + +using Syntriax.Engine.Network.Abstract; + +namespace Syntriax.Engine.Network; + +public class NetworkManager : NetworkBehaviour, INetworkManager +{ + private BehaviourCollector entities = null!; + + private static uint networkIdIndex = 0; + + protected override void OnInitialize() + { + base.OnInitialize(); + + NetworkId = networkIdIndex++; + + entities = new(GameObject.GameManager); + foreach (var entity in entities) + entity.NetworkId = networkIdIndex++; + + entities.OnCollected += OnCollected; + } + + private void OnCollected(BehaviourCollector collector, INetworkEntity entity) + => entity.NetworkId = networkIdIndex++; +} diff --git a/Game/Network/NetworkServer.cs b/Game/Network/NetworkServer.cs new file mode 100644 index 0000000..a8e26ec --- /dev/null +++ b/Game/Network/NetworkServer.cs @@ -0,0 +1,30 @@ +using Syntriax.Engine.Network.Abstract; + +namespace Syntriax.Engine.Network; + +public class NetworkServer : NetworkBase, INetworkCommunicatorServer +{ + public string Password { get; private set; } = string.Empty; + public int MaxConnectionCount { get; private set; } = 0; + public int Port { get; private set; } = 8888; + + public NetworkServer() : base() + { + Listener.ConnectionRequestEvent += request => + { + if (Manager.ConnectedPeersCount < MaxConnectionCount) + request.AcceptIfKey(Password); + else + request.Reject(); + }; + } + + public void Start(int port, int maxConnectionCount, string? password = null) + { + Password = password ?? string.Empty; + MaxConnectionCount = maxConnectionCount; + Port = port; + + Manager.Start(port); + } +}