This commit is contained in:
Syntriax 2024-07-15 17:00:04 +03:00
parent 73ae55e1d4
commit 91d301677f
14 changed files with 363 additions and 0 deletions

View File

@ -21,6 +21,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Apos.Shapes" Version="0.2.3" />
<PackageReference Include="LiteNetLib" Version="1.2.0" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
</ItemGroup>

View File

@ -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; }
}

View File

@ -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<T>(INetworkEntity networkEntity, EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable;
void UnregisterEntityPacketListener<T>(INetworkEntity networkEntity, EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable;
void SendEntityPacket<T>(INetworkEntity networkEntity, T packet, params NetPeer[] netPeer) where T : INetSerializable;
delegate void EntityPacketReceivedDelegate(INetworkEntity networkEntity, object packet, NetPeer netPeer);
}

View File

@ -0,0 +1,6 @@
namespace Syntriax.Engine.Network.Abstract;
public interface INetworkCommunicatorClient : INetworkCommunicator
{
void Connect(string address, int port, string? password = null);
}

View File

@ -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);
}

View File

@ -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<T>(PacketReceivedDelegate onPacketReceived) where T : INetSerializable;
void UnregisterPacketListener<T>(PacketReceivedDelegate onPacketReceived) where T : INetSerializable;
void SendPacket<T>(T packet) where T : INetSerializable;
}

View File

@ -0,0 +1,10 @@
namespace Syntriax.Engine.Network.Abstract;
public interface INetworkManager
{
// Action<IGameObject>? OnNetworkGameObjectInstantiated { get; set; }
INetworkCommunicator NetworkCommunicator { get; }
// Task<T> Instantiate<T>(params object?[]? args) where T : class, IGameObject;
}

View File

@ -0,0 +1,21 @@
using LiteNetLib.Utils;
namespace Syntriax.Engine.Network.Abstract;
public class NetworkPacket<T>()
: 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);
}
}

109
Game/Network/NetworkBase.cs Normal file
View File

@ -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<uint, INetworkEntity> networkEntities = [];
private readonly Dictionary<Type, List<INetworkCommunicator.EntityPacketReceivedDelegate>> callbacks = [];
private BehaviourCollector<INetworkEntity> 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<INetworkEntity> sender, INetworkEntity behaviourCollected)
=> networkEntities.Add(behaviourCollected.NetworkId, behaviourCollected);
private void OnNetworkEntityRemoved(BehaviourCollector<INetworkEntity> 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<T>(INetworkEntity networkEntity, INetworkCommunicator.EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable
{
if (!callbacks.TryGetValue(typeof(T), out var list))
{
list = [];
callbacks.Add(typeof(T), list);
netPacketProcessor.SubscribeReusable<NetworkPacket<T>, NetPeer>(OnPacketReceived);
}
if (list.Contains(onPacketReceived))
return;
list.Add(onPacketReceived);
}
public void UnregisterEntityPacketListener<T>(INetworkEntity networkEntity, INetworkCommunicator.EntityPacketReceivedDelegate onPacketReceived) where T : INetSerializable
{
if (!callbacks.TryGetValue(typeof(T), out var list))
return;
list.Remove(onPacketReceived);
}
public void SendEntityPacket<T>(INetworkEntity networkEntity, T packet, params NetPeer[] netPeers) where T : INetSerializable
{
NetworkPacket<T> 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<T>(NetworkPacket<T> 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);
}
}

View File

@ -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<INetworkCommunicator>()
?? GameObject.GameManager.FindBehaviour<INetworkCommunicator>()
?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}.");
if (NetworkCommunicator is INetworkCommunicatorClient client)
{
IsClient = true;
return;
}
IsServer = true;
}
public void RegisterPacketListener<T>(INetworkEntity.PacketReceivedDelegate onPacketReceived) where T : INetSerializable
private void OnEntityPacketReceived(INetworkEntity networkEntity, object packet, NetPeer netPeer)
{
throw new NotImplementedException();
}
public void UnregisterPacketListener<T>(INetworkEntity.PacketReceivedDelegate onPacketReceived) where T : INetSerializable
{
throw new NotImplementedException();
}
public void SendPacket<T>(T packet) where T : INetSerializable
{
throw new NotImplementedException();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,28 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Network.Abstract;
namespace Syntriax.Engine.Network;
public class NetworkManager : NetworkBehaviour, INetworkManager
{
private BehaviourCollector<INetworkEntity> 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<INetworkEntity> collector, INetworkEntity entity)
=> entity.NetworkId = networkIdIndex++;
}

View File

@ -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);
}
}