diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj b/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj new file mode 100644 index 0000000..3a34cb3 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj @@ -0,0 +1,19 @@ + + + + net9.0 + enable + enable + Syntriax.Engine.Systems.Network + Syntriax.Engine.Integration.LiteNetLib + + + + + + + + + + + diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs new file mode 100644 index 0000000..0967d57 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs @@ -0,0 +1,85 @@ +using System.Net.Sockets; + +using LiteNetLib; +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Debug; + +namespace Syntriax.Engine.Systems.Network; + +public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicatorClient +{ + private readonly NetDataWriter netDataWriter = new(); + + private CancellationTokenSource? cancellationTokenSource = null; + + protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibServerConnection(peer); + + public INetworkCommunicatorClient Connect(string address, int port, string? password = null) + { + if (!UniverseObject.IsInUniverse) + throw new($"{nameof(LiteNetLibClient)} must be in an universe to connect"); + + password ??= string.Empty; + + // Okay, for some reason sometimes LiteNetLib goes dumb when the server hostname has IPv6 address as well as IPv4 + // but the client doesn't support IPv6, it still tries to use the v6 unless we explicitly tell the ip to connect to, + // which fails to connect... So for the time being I am preferring IPv4 below over IPv6 for clients. + // TODO: I think this is something that happens on Linux only? I need to check on Windows as well just to be sure. + System.Net.IPAddress[] addresses = System.Net.Dns.GetHostAddresses(address); + string connectionAddress = addresses.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork, addresses[0]).ToString(); + + logger?.Log(this, $"Connecting to server at '{address}:{port}' with password '{password}'"); + logger?.Log(this, $"Resolved address for {address}: {connectionAddress}"); + + Manager.Start(); + Manager.Connect(connectionAddress, port, password); + + return this; + } + + public INetworkCommunicatorClient SendToServer(T packet, PacketDelivery packetDelivery) where T : class, new() + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + + switch (packetDelivery) + { + case PacketDelivery.ReliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + case PacketDelivery.UnreliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break; + case PacketDelivery.ReliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break; + case PacketDelivery.UnreliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break; + default: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + } + + return this; + } + + protected override void OnEnteredUniverse(IUniverse universe) + { + base.OnEnteredUniverse(universe); + + cancellationTokenSource = new CancellationTokenSource(); + PollEvents(cancellationTokenSource.Token); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + base.OnExitedUniverse(universe); + cancellationTokenSource?.Cancel(); + } + + /// + /// Client needs to send everything as soon as possible so + /// the events are polled a separate thread running constantly + /// + private async void PollEvents(CancellationToken cancellationToken) => await Task.Run(() => + { + while (true) + { + Manager.PollEvents(); + Thread.Sleep(1); + } + }, cancellationToken); +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClientConnection.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClientConnection.cs new file mode 100644 index 0000000..5e2408f --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClientConnection.cs @@ -0,0 +1,16 @@ +using LiteNetLib; + +namespace Syntriax.Engine.Systems.Network; + +public record class LiteNetLibClientConnection(NetPeer NetPeer) : IConnection +{ + public string Id { get; } = NetPeer.Id.ToString(); + + public float Ping => NetPeer.Ping * .001f; + public float RoundTrip => NetPeer.RoundTripTime * .001f; + + public int PingMs => NetPeer.Ping; + public int RoundTripMs => NetPeer.RoundTripTime; + + public override string ToString() => $"Connection({Id})"; +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs new file mode 100644 index 0000000..ed14f12 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs @@ -0,0 +1,185 @@ +using System.Reflection; + +using LiteNetLib; +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Debug; + +namespace Syntriax.Engine.Systems.Network; + +public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicator +{ + protected readonly NetPacketProcessor netPacketProcessor = new(); + + private readonly Dictionary> listeners = []; + private readonly Dictionary _connections = []; + + private readonly Dictionary localPeerIdToConnectionDictionary = []; + + protected ILogger? logger = null; + + public IReadOnlyDictionary Connections => _connections; + public EventBasedNetListener Listener { get; private set; } = null!; + public NetManager Manager { get; private set; } = null!; + + public Event OnConnectionEstablished { get; } = new(); + public Event OnConnectionAbolished { get; } = new(); + + public INetworkCommunicator Stop() + { + Manager.Stop(); + return this; + } + + protected override void OnEnteredUniverse(IUniverse universe) + { + base.OnEnteredUniverse(universe); + logger = universe.FindBehaviour(); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + base.OnExitedUniverse(universe); + logger = null; + Stop(); + } + + protected virtual void OnPacketArrived(T packet, NetPeer peer) where T : INetworkPacket + { + if (!listeners.TryGetValue(typeof(T), out Event? @event)) + return; + + @event.Invoke(localPeerIdToConnectionDictionary[peer.Id], packet); + } + + private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) + { + try { netPacketProcessor.ReadAllPackets(reader, peer); } + catch (Exception exception) { logger?.LogException(this, exception, force: true); } + } + + protected abstract IConnection GetConnection(NetPeer peer); + private void ConnectionEstablished(NetPeer peer) + { + IConnection connection = GetConnection(peer); + + localPeerIdToConnectionDictionary.Add(peer.Id, connection); + _connections.Add(connection.Id, connection); + + logger?.Log(this, $"Connection established with ip '{peer.Address}' and id '{connection.Id}'"); + OnConnectionEstablished.Invoke(this, connection); + } + + private void ConnectionAbolished(NetPeer peer, DisconnectInfo disconnectInfo) + { + if (!localPeerIdToConnectionDictionary.TryGetValue(peer.Id, out IConnection? connection)) + return; + + localPeerIdToConnectionDictionary.Remove(peer.Id); + _connections.Remove(connection.Id); + + logger?.Log(this, $"Connection abolished with ip '{peer.Address}' and id '{connection.Id}'"); + OnConnectionAbolished.Invoke(this, connection); + } + + private void SetupPackets() + { + // Find network packets implementing INetworkPacket + IEnumerable packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) + .Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType); + + MethodInfo subscribeReusableMethod = typeof(NetPacketProcessor) + .GetMethods() + .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable) && + m.GetParameters().Length == 1 && + m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) + )!; + + MethodInfo registerNestedTypeMethod = typeof(NetPacketProcessor) + .GetMethods() + .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.RegisterNestedType) && + m.GetParameters().Length == 0 + )!; + + MethodInfo onPacketArrivedMethod = typeof(LiteNetLibCommunicatorBase) + .GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance)!; + + // Register all network packets by calling the methods bellow where T is our found network packet type + // NetPacketProcessor.SubscribeReusable(Action onReceive) + // NetPacketProcessor.RegisterNestedType() + foreach (Type packetType in packetTypes) + { + if (!packetType.IsClass) + { + MethodInfo genericRegisterNestedTypeMethod = registerNestedTypeMethod.MakeGenericMethod(packetType); + genericRegisterNestedTypeMethod.Invoke(netPacketProcessor, []); + continue; + } + + MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType); + + Type delegateType = typeof(Action<,>).MakeGenericType(packetType, typeof(NetPeer)); + Delegate delegateHandler = Delegate.CreateDelegate(delegateType, this, genericOnPacketArrivedMethod); + + MethodInfo genericSubscribeReusableMethod = subscribeReusableMethod.MakeGenericMethod(packetType, typeof(NetPeer)); + genericSubscribeReusableMethod.Invoke(netPacketProcessor, [delegateHandler]); + } + } + + public LiteNetLibCommunicatorBase() + { + Listener = new EventBasedNetListener(); + Manager = new NetManager(Listener); + + Listener.NetworkReceiveEvent += NetworkReceiveEvent; + Listener.PeerConnectedEvent += ConnectionEstablished; + Listener.PeerDisconnectedEvent += ConnectionAbolished; + + SetupEnginePackets(); + SetupPackets(); + } + + private void SetupEnginePackets() + { + // I know, ugly af. I need to find a better way + netPacketProcessor.RegisterNestedType(AABBNetPacker.Write, AABBNetPacker.Read); + netPacketProcessor.RegisterNestedType(CircleNetPacker.Write, CircleNetPacker.Read); + netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.Read); + netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read); + netPacketProcessor.RegisterNestedType(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read); + netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read); + netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.Read); + netPacketProcessor.RegisterNestedType(Projection1DNetPacker.Write, Projection1DNetPacker.Read); + netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.Read); + netPacketProcessor.RegisterNestedType(Shape2DNetPacker.Write, Shape2DNetPacker.Read); + netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read); + netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read); + netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.Read); + } + + public INetworkCommunicator SubscribeToPackets(Event.EventHandler callback) + { + Type type = typeof(T); + if (!listeners.TryGetValue(type, out Event? @event)) + { + @event = new(); + listeners.Add(type, @event); + } + + @event.AddListener(EventDelegateWrapper(callback)); + return this; + } + + public INetworkCommunicator UnsubscribeFromPackets(Event.EventHandler callback) + { + Type type = typeof(T); + if (!listeners.TryGetValue(type, out Event? @event)) + return this; + + @event.RemoveListener(EventDelegateWrapper(callback)); + return this; + } + + private static Event.EventHandler EventDelegateWrapper(Event.EventHandler callback) => (sender, @object) => callback.Invoke(sender, (T)@object); +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs new file mode 100644 index 0000000..d3fad8e --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs @@ -0,0 +1,102 @@ +using LiteNetLib; +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Debug; + +namespace Syntriax.Engine.Systems.Network; + +public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicatorServer +{ + public string Password { get; private set; } = string.Empty; + public int MaxConnectionCount { get; private set; } = 2; + public int Port { get; private set; } = 8888; + + private readonly NetDataWriter netDataWriter = new(); + + public LiteNetLibServer() : this(8888, 2) { } + public LiteNetLibServer(int port, int maxConnectionCount) : base() + { + MaxConnectionCount = maxConnectionCount; + Port = port; + + Listener.ConnectionRequestEvent += OnConnectionRequest; + } + + protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibClientConnection(peer); + + private void OnConnectionRequest(ConnectionRequest request) + { + logger?.Log(this, $"Connection request from ip {request.RemoteEndPoint}"); + logger?.Log(this, $"Current connection count: {Connections.Count}"); + if (Manager.ConnectedPeersCount < MaxConnectionCount) + request.AcceptIfKey(Password); + else + request.Reject(); + } + + public INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null) + { + if (!UniverseObject.IsInUniverse) + throw new($"{nameof(LiteNetLibServer)} must be in an universe to start"); + + Password = password ?? string.Empty; + MaxConnectionCount = maxConnectionCount; + Port = port; + + logger?.Log(this, $"Starting server on port '{port}' with password '{Password}' and max connection count '{maxConnectionCount}'"); + logger?.Log(this, $"Server status: {(Manager.Start(port) ? "Active" : "Failed")}"); + + return this; + } + + public INetworkCommunicatorServer SendToClient(IConnection connection, T packet, PacketDelivery packetDelivery) where T : class, new() + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + + if (Manager.ConnectedPeerList.FirstOrDefault(p => p.Id.CompareTo(connection.Id) == 0) is not NetPeer netPeer) + throw new($"Peer {connection} couldn't be found."); + + switch (packetDelivery) + { + case PacketDelivery.ReliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + case PacketDelivery.UnreliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break; + case PacketDelivery.ReliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break; + case PacketDelivery.UnreliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break; + default: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + } + + return this; + } + + public INetworkCommunicatorServer SendToAll(T packet, PacketDelivery packetDelivery) where T : class, new() + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + + switch (packetDelivery) + { + case PacketDelivery.ReliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break; + case PacketDelivery.UnreliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Sequenced); break; + case PacketDelivery.ReliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableUnordered); break; + case PacketDelivery.UnreliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Unreliable); break; + default: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break; + } + return this; + } + + private void PollEvents(IUniverse sender, IUniverse.UpdateArguments args) => Manager.PollEvents(); + + protected override void OnEnteredUniverse(IUniverse universe) + { + base.OnEnteredUniverse(universe); + universe.OnPostUpdate.AddListener(PollEvents); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + base.OnExitedUniverse(universe); + universe.OnPostUpdate.RemoveListener(PollEvents); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServerConnection.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServerConnection.cs new file mode 100644 index 0000000..66f8f3b --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServerConnection.cs @@ -0,0 +1,16 @@ +using LiteNetLib; + +namespace Syntriax.Engine.Systems.Network; + +public record class LiteNetLibServerConnection(NetPeer NetPeer) : IConnection +{ + public string Id => NetPeer.RemoteId.ToString(); + + public float Ping => NetPeer.Ping * .001f; + public float RoundTrip => NetPeer.RoundTripTime * .001f; + + public int PingMs => NetPeer.Ping; + public int RoundTripMs => NetPeer.RoundTripTime; + + public override string ToString() => $"Connection({Id})"; +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/AABBNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/AABBNetPacker.cs new file mode 100644 index 0000000..e8ed56e --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/AABBNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class AABBNetPacker +{ + internal static void Write(NetDataWriter writer, AABB data) + { + Vector2DNetPacker.Write(writer, data.LowerBoundary); + Vector2DNetPacker.Write(writer, data.UpperBoundary); + } + + internal static AABB Read(NetDataReader reader) + { + Vector2D lowerBoundary = Vector2DNetPacker.Read(reader); + Vector2D upperBoundary = Vector2DNetPacker.Read(reader); + + return new AABB(lowerBoundary, upperBoundary); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/CircleNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/CircleNetPacker.cs new file mode 100644 index 0000000..5545dca --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/CircleNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class CircleNetPacker +{ + internal static void Write(NetDataWriter writer, Circle data) + { + Vector2DNetPacker.Write(writer, data.Center); + writer.Put(data.Radius); + } + + internal static Circle Read(NetDataReader reader) + { + Vector2D center = Vector2DNetPacker.Read(reader); + float radius = reader.GetFloat(); + + return new Circle(center, radius); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorHSVNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorHSVNetPacker.cs new file mode 100644 index 0000000..af868c8 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorHSVNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class ColorHSVNetPacker +{ + internal static void Write(NetDataWriter writer, ColorHSV data) + { + writer.Put(data.Hue); + writer.Put(data.Saturation); + writer.Put(data.Value); + } + + internal static ColorHSV Read(NetDataReader reader) + { + float hue = reader.GetFloat(); + float saturation = reader.GetFloat(); + float value = reader.GetFloat(); + + return new ColorHSV(hue, saturation, value); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBANetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBANetPacker.cs new file mode 100644 index 0000000..0b20f06 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBANetPacker.cs @@ -0,0 +1,26 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class ColorRGBANetPacker +{ + internal static void Write(NetDataWriter writer, ColorRGBA data) + { + writer.Put(data.R); + writer.Put(data.G); + writer.Put(data.B); + writer.Put(data.A); + } + + internal static ColorRGBA Read(NetDataReader reader) + { + byte red = reader.GetByte(); + byte green = reader.GetByte(); + byte blue = reader.GetByte(); + byte alpha = reader.GetByte(); + + return new ColorRGBA(red, green, blue, alpha); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBNetPacker.cs new file mode 100644 index 0000000..46770e1 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class ColorRGBNetPacker +{ + internal static void Write(NetDataWriter writer, ColorRGB data) + { + writer.Put(data.R); + writer.Put(data.G); + writer.Put(data.B); + } + + internal static ColorRGB Read(NetDataReader reader) + { + byte red = reader.GetByte(); + byte green = reader.GetByte(); + byte blue = reader.GetByte(); + + return new ColorRGB(red, green, blue); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DEquationNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DEquationNetPacker.cs new file mode 100644 index 0000000..3bcfe70 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DEquationNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class Line2DEquationNetPacker +{ + internal static void Write(NetDataWriter writer, Line2DEquation data) + { + writer.Put(data.Slope); + writer.Put(data.OffsetY); + } + + internal static Line2DEquation Read(NetDataReader reader) + { + float slope = reader.GetFloat(); + float offsetY = reader.GetFloat(); + + return new Line2DEquation(slope, offsetY); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DNetPacker.cs new file mode 100644 index 0000000..899b0f7 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class Line2DNetPacker +{ + internal static void Write(NetDataWriter writer, Line2D data) + { + Vector2DNetPacker.Write(writer, data.From); + Vector2DNetPacker.Write(writer, data.To); + } + + internal static Line2D Read(NetDataReader reader) + { + Vector2D from = Vector2DNetPacker.Read(reader); + Vector2D to = Vector2DNetPacker.Read(reader); + + return new Line2D(from, to); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Projection1DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Projection1DNetPacker.cs new file mode 100644 index 0000000..0a356ef --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Projection1DNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class Projection1DNetPacker +{ + internal static void Write(NetDataWriter writer, Projection1D data) + { + writer.Put(data.Min); + writer.Put(data.Max); + } + + internal static Projection1D Read(NetDataReader reader) + { + float min = reader.GetFloat(); + float max = reader.GetFloat(); + + return new Projection1D(min, max); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/QuaternionNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/QuaternionNetPacker.cs new file mode 100644 index 0000000..a1241be --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/QuaternionNetPacker.cs @@ -0,0 +1,26 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class QuaternionNetPacker +{ + internal static void Write(NetDataWriter writer, Quaternion data) + { + writer.Put(data.X); + writer.Put(data.Y); + writer.Put(data.Z); + writer.Put(data.W); + } + + internal static Quaternion Read(NetDataReader reader) + { + float x = reader.GetFloat(); + float y = reader.GetFloat(); + float z = reader.GetFloat(); + float w = reader.GetFloat(); + + return new Quaternion(x, y, z, w); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Shape2DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Shape2DNetPacker.cs new file mode 100644 index 0000000..525185f --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Shape2DNetPacker.cs @@ -0,0 +1,26 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class Shape2DNetPacker +{ + internal static void Write(NetDataWriter writer, Shape2D data) + { + writer.Put(data.Vertices.Count); + foreach (Vector2D vertex in data.Vertices) + Vector2DNetPacker.Write(writer, vertex); + } + + internal static Shape2D Read(NetDataReader reader) + { + int verticesCount = reader.GetInt(); + List vertices = new(verticesCount); + + for (int i = 0; i < verticesCount; i++) + vertices.Add(Vector2DNetPacker.Read(reader)); + + return new Shape2D(vertices); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/TriangleNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/TriangleNetPacker.cs new file mode 100644 index 0000000..8c22746 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/TriangleNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class TriangleNetPacker +{ + internal static void Write(NetDataWriter writer, Triangle data) + { + Vector2DNetPacker.Write(writer, data.A); + Vector2DNetPacker.Write(writer, data.B); + Vector2DNetPacker.Write(writer, data.C); + } + + internal static Triangle Read(NetDataReader reader) + { + Vector2D a = Vector2DNetPacker.Read(reader); + Vector2D b = Vector2DNetPacker.Read(reader); + Vector2D c = Vector2DNetPacker.Read(reader); + + return new Triangle(a, b, c); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector2DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector2DNetPacker.cs new file mode 100644 index 0000000..5f1485f --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector2DNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class Vector2DNetPacker +{ + internal static void Write(NetDataWriter writer, Vector2D data) + { + writer.Put(data.X); + writer.Put(data.Y); + } + + internal static Vector2D Read(NetDataReader reader) + { + float x = reader.GetFloat(); + float y = reader.GetFloat(); + + return new Vector2D(x, y); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector3DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector3DNetPacker.cs new file mode 100644 index 0000000..d594104 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector3DNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Systems.Network; + +internal static class Vector3DNetPacker +{ + internal static void Write(NetDataWriter writer, Vector3D data) + { + writer.Put(data.X); + writer.Put(data.Y); + writer.Put(data.Z); + } + + internal static Vector3D Read(NetDataReader reader) + { + float x = reader.GetFloat(); + float y = reader.GetFloat(); + float z = reader.GetFloat(); + + return new Vector3D(x, y, z); + } +} diff --git a/Engine.sln b/Engine.sln index 4f5da96..b81932e 100644 --- a/Engine.sln +++ b/Engine.sln @@ -19,7 +19,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet", "Engine.Serial EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{823D4020-332D-2C13-F261-6F510F11A57E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame", "Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{C3438D33-0879-44E4-9DF0-D29F5621C44C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.MonoGame", "Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{C3438D33-0879-44E4-9DF0-D29F5621C44C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Integration", "Engine.Integration", "{3122C4BF-14AF-E0C0-27A2-43B3E062692D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.LiteNetLib", "Engine.Integration\Engine.Integration.LiteNetLib\Engine.Integration.LiteNetLib.csproj", "{121A7C66-1691-4DA5-B070-A681A83779AC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -115,6 +119,18 @@ Global {C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x64.Build.0 = Release|Any CPU {C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.ActiveCfg = Release|Any CPU {C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.Build.0 = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x64.ActiveCfg = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x64.Build.0 = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x86.ActiveCfg = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x86.Build.0 = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|Any CPU.Build.0 = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x64.ActiveCfg = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x64.Build.0 = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x86.ActiveCfg = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -123,5 +139,6 @@ Global {E9D1CDC3-5BFF-4C87-AFEB-6CE372709176} = {F88E129A-9A47-4D27-96EE-6EC02F79594B} {3D852C92-BC14-4893-AEF2-50612DAFCD8F} = {F88E129A-9A47-4D27-96EE-6EC02F79594B} {C3438D33-0879-44E4-9DF0-D29F5621C44C} = {823D4020-332D-2C13-F261-6F510F11A57E} + {121A7C66-1691-4DA5-B070-A681A83779AC} = {3122C4BF-14AF-E0C0-27A2-43B3E062692D} EndGlobalSection EndGlobal