From 78a84f1087e46c88f5733d5592fd45284e986460 Mon Sep 17 00:00:00 2001 From: Sebastian Jura <22455534+CrosRoad95@users.noreply.github.com> Date: Thu, 16 May 2024 20:07:14 +0200 Subject: [PATCH 1/4] Add POC of class that goal is to simulate client environment --- .../Element/SetElementPositionRpcPacket.cs | 14 ++++++- SlipeServer.Packets/Reader/PacketReader.cs | 1 + SlipeServer.Server.TestTools/TestingClient.cs | 39 +++++++++++++++++++ SlipeServer.Server.TestTools/TestingPlayer.cs | 33 ++++++++++++++++ .../Integration/HostingTests.cs | 14 +++++++ SlipeServer.Server/Clients/Client.cs | 4 +- 6 files changed, 101 insertions(+), 4 deletions(-) diff --git a/SlipeServer.Packets/Definitions/Lua/ElementRpc/Element/SetElementPositionRpcPacket.cs b/SlipeServer.Packets/Definitions/Lua/ElementRpc/Element/SetElementPositionRpcPacket.cs index 4f315bcd..c8f28ece 100644 --- a/SlipeServer.Packets/Definitions/Lua/ElementRpc/Element/SetElementPositionRpcPacket.cs +++ b/SlipeServer.Packets/Definitions/Lua/ElementRpc/Element/SetElementPositionRpcPacket.cs @@ -1,5 +1,6 @@ using SlipeServer.Packets.Builder; using SlipeServer.Packets.Enums; +using SlipeServer.Packets.Reader; using SlipeServer.Packets.Structs; using System; using System.Numerics; @@ -15,7 +16,7 @@ public class SetElementPositionRpcPacket : Packet public ElementId ElementId { get; set; } public byte TimeContext { get; set; } public Vector3 Position { get; set; } - public bool IsWarp { get; } + public bool IsWarp { get; set; } public SetElementPositionRpcPacket() { @@ -32,7 +33,16 @@ public SetElementPositionRpcPacket(ElementId elementId, byte timeContext, Vector public override void Read(byte[] bytes) { - throw new NotSupportedException(); + var reader = new PacketReader(bytes); + + var rpcFunction = (ElementRpcFunction)reader.GetByte(); + if (rpcFunction != ElementRpcFunction.SET_ELEMENT_POSITION) + throw new InvalidOperationException($"Invalid rpcFunction, expected SET_ELEMENT_POSITION, got: {rpcFunction}"); + + this.ElementId = reader.GetElementId(); + this.Position = reader.GetVector3(); + this.TimeContext = reader.GetByte(); + this.IsWarp = reader.IsFinishedReading ? true : false; } public override byte[] Write() diff --git a/SlipeServer.Packets/Reader/PacketReader.cs b/SlipeServer.Packets/Reader/PacketReader.cs index 53e5e6e7..41e3a0b8 100644 --- a/SlipeServer.Packets/Reader/PacketReader.cs +++ b/SlipeServer.Packets/Reader/PacketReader.cs @@ -1,6 +1,7 @@ using SlipeServer.Packets.Structs; using System; using System.Linq; +using System.Numerics; using System.Text; namespace SlipeServer.Packets.Reader; diff --git a/SlipeServer.Server.TestTools/TestingClient.cs b/SlipeServer.Server.TestTools/TestingClient.cs index fcf7bdde..8d33efa0 100644 --- a/SlipeServer.Server.TestTools/TestingClient.cs +++ b/SlipeServer.Server.TestTools/TestingClient.cs @@ -1,6 +1,12 @@ using SlipeServer.Net.Wrappers; +using SlipeServer.Packets; +using SlipeServer.Packets.Definitions.Lua.ElementRpc; +using SlipeServer.Packets.Definitions.Lua.ElementRpc.Element; +using SlipeServer.Packets.Enums; using SlipeServer.Server.Clients; using SlipeServer.Server.Elements; +using System; +using System.Net.Sockets; namespace SlipeServer.Server.TestTools; @@ -8,10 +14,43 @@ public class TestingClient : Client { public uint Address { get; private set; } + public event Action PacketSent; + public TestingClient(uint address, INetWrapper netWrapper, Player player) : base(address, netWrapper, player) { this.Address = address; this.ConnectionState = Enums.ClientConnectionState.Joined; } + + public override void SendPacket(Packet packet) + { + base.SendPacket(packet); + PacketSent?.Invoke(this, packet); + } + + public override void SendPacket(PacketId packetId, byte[] data, PacketPriority priority = PacketPriority.Medium, PacketReliability reliability = PacketReliability.Unreliable) + { + base.SendPacket(packetId, data, priority, reliability); + switch (packetId) + { + case PacketId.PACKET_ID_LUA_ELEMENT_RPC: + var elementRpcFunction = (ElementRpcFunction)data[0]; + switch (elementRpcFunction) + { + case ElementRpcFunction.SET_ELEMENT_POSITION: + HandleElementSetElementPositionPacket(data); + break; + } + ; + break; + } + } + + private void HandleElementSetElementPositionPacket(byte[] data) + { + var packet = new SetElementPositionRpcPacket(); + packet.Read(data); + PacketSent?.Invoke(this, packet); + } } diff --git a/SlipeServer.Server.TestTools/TestingPlayer.cs b/SlipeServer.Server.TestTools/TestingPlayer.cs index 131632dd..f78a8543 100644 --- a/SlipeServer.Server.TestTools/TestingPlayer.cs +++ b/SlipeServer.Server.TestTools/TestingPlayer.cs @@ -1,6 +1,9 @@ using Moq; using SlipeServer.Net.Wrappers; +using SlipeServer.Packets.Definitions.Lua.ElementRpc.Element; using SlipeServer.Server.Elements; +using System; +using System.Numerics; namespace SlipeServer.Server.TestTools; @@ -14,3 +17,33 @@ public static Player CreateStandalone() return player; } } + +public class ClientEnvironment +{ + public TestingPlayer Player { get; } + + public Vector3 Position { get; private set; } + + public ClientEnvironment(TestingPlayer testingPlayer) + { + this.Player = testingPlayer; + var testingClient = testingPlayer.Client as TestingClient; + testingClient.PacketSent += HandlePacketSent; + } + + private void HandlePacketSent(TestingClient client, Packets.Packet packet) + { + switch (packet) + { + case SetElementPositionRpcPacket setElementPositionRpcPacket: + if (setElementPositionRpcPacket.ElementId == this.Player.Id) + { + this.Position = setElementPositionRpcPacket.Position; + } else + { + throw new Exception("Element not found."); + } + break; + } + } +} diff --git a/SlipeServer.Server.Tests/Integration/HostingTests.cs b/SlipeServer.Server.Tests/Integration/HostingTests.cs index a24f203e..accd1f76 100644 --- a/SlipeServer.Server.Tests/Integration/HostingTests.cs +++ b/SlipeServer.Server.Tests/Integration/HostingTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using System.Threading; using FluentAssertions; +using SlipeServer.Server.Elements; namespace SlipeServer.Server.Tests.Integration; @@ -30,6 +31,19 @@ public void HostingShouldWork() sampleService.Started.Should().BeTrue(); sampleService.Stopped.Should().BeTrue(); } + + [Fact] + public void ClientInterfaceShouldWork() + { + using var hosting = new TestingServerHosting(); + + var player = hosting.Server.AddFakePlayer(); + var clientInterface = new ClientEnvironment(player); + + player.Position = new System.Numerics.Vector3(3, 3, 3); + + clientInterface.Position.Should().Be(player.Position); + } } public class SampleHostedService : IHostedService diff --git a/SlipeServer.Server/Clients/Client.cs b/SlipeServer.Server/Clients/Client.cs index 4102356a..ce0b43c5 100644 --- a/SlipeServer.Server/Clients/Client.cs +++ b/SlipeServer.Server/Clients/Client.cs @@ -92,7 +92,7 @@ public Client(uint binaryAddress, INetWrapper netWrapper, TPlayer player) /// Sends a single packet to the client /// /// - public void SendPacket(Packet packet) + public virtual void SendPacket(Packet packet) { if (!CanSendPacket(packet.PacketId)) return; @@ -106,7 +106,7 @@ public void SendPacket(Packet packet) /// Sends a single packet to the client /// /// - public void SendPacket(PacketId packetId, byte[] data, PacketPriority priority = PacketPriority.Medium, PacketReliability reliability = PacketReliability.Unreliable) + public virtual void SendPacket(PacketId packetId, byte[] data, PacketPriority priority = PacketPriority.Medium, PacketReliability reliability = PacketReliability.Unreliable) { if (!CanSendPacket(packetId)) return; From f4728b09f321d1442f0d68d8ccf5c30500bdfd4b Mon Sep 17 00:00:00 2001 From: Sebastian Jura <22455534+CrosRoad95@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:03:11 +0100 Subject: [PATCH 2/4] Make pure sync for test purpose work to some extent --- SlipeServer.Console/Logic/ServerTestLogic.cs | 2 +- .../Definitions/Sync/PlayerPureSyncPacket.cs | 9 ++- .../CameraOrientationStructure.cs | 10 ++- SlipeServer.Server.TestTools/TestingPlayer.cs | 77 ++++++++++++++++--- SlipeServer.Server.TestTools/TestingServer.cs | 2 + .../Integration/ClientEnvironmentTests.cs | 29 ++++++- .../Unit/Elements/ElementTests.cs | 4 +- SlipeServer.Server/ElementConcepts/Camera.cs | 2 +- SlipeServer.Server/Elements/Element.cs | 25 +----- SlipeServer.Server/Elements/TimeContext.cs | 48 ++++++++++++ .../Relaying/PedPropertyRelayingExtensions.cs | 2 +- .../VehiclePropertyRelayingExtensions.cs | 8 +- SlipeServer.Server/MtaServer.cs | 2 +- .../Factories/ElementPacketFactory.cs | 10 +-- .../Factories/PedPacketFactory.cs | 4 +- .../Factories/PlayerPacketFactory.cs | 4 +- .../Factories/VehiclePacketFactory.cs | 4 +- 17 files changed, 179 insertions(+), 63 deletions(-) create mode 100644 SlipeServer.Server/Elements/TimeContext.cs diff --git a/SlipeServer.Console/Logic/ServerTestLogic.cs b/SlipeServer.Console/Logic/ServerTestLogic.cs index fbb4c753..60f1c4bd 100644 --- a/SlipeServer.Console/Logic/ServerTestLogic.cs +++ b/SlipeServer.Console/Logic/ServerTestLogic.cs @@ -547,7 +547,7 @@ void Player_Disconnected(Player sender, PlayerQuitEventArgs e) }; this.commandService.AddCommand("increment").Triggered += (source, args) - => args.Player.GetAndIncrementTimeContext(); + => args.Player.TimeContext.GetAndIncrement(); this.commandService.AddCommand("resendmodpackets").Triggered += (source, args) => args.Player.ResendModPackets(); diff --git a/SlipeServer.Packets/Definitions/Sync/PlayerPureSyncPacket.cs b/SlipeServer.Packets/Definitions/Sync/PlayerPureSyncPacket.cs index e3716e5a..8c61f243 100644 --- a/SlipeServer.Packets/Definitions/Sync/PlayerPureSyncPacket.cs +++ b/SlipeServer.Packets/Definitions/Sync/PlayerPureSyncPacket.cs @@ -6,6 +6,7 @@ using SlipeServer.Packets.Reader; using SlipeServer.Packets.Constants; using SlipeServer.Packets.Structs; +using System.Reflection.PortableExecutable; namespace SlipeServer.Packets.Definitions.Sync; @@ -113,9 +114,9 @@ public override byte[] Write() { var builder = new PacketBuilder(); - builder.Write(this.PlayerId); + //builder.Write(this.PlayerId); builder.Write(this.TimeContext); - builder.WriteCompressed(this.Latency); + //builder.WriteCompressed(this.Latency); this.KeySync.Write(builder); this.SyncFlags.Write(builder); @@ -135,7 +136,9 @@ public override byte[] Write() builder.WritePlayerHealth(this.Health); builder.WritePlayerArmor(this.Armor); - builder.WriteFloatFromBits(this.CameraRotation, 12, -MathF.PI, MathF.PI, true, false); + builder.WriteFloatFromBits(this.CameraRotation, 12, -MathF.PI, MathF.PI, false, true); + this.CameraOrientation = new CameraOrientationStructure(this.Position); + this.CameraOrientation.Write(builder); if (this.SyncFlags.HasAWeapon) { diff --git a/SlipeServer.Packets/SyncStructures/CameraOrientationStructure.cs b/SlipeServer.Packets/SyncStructures/CameraOrientationStructure.cs index a57748c4..59654ec9 100644 --- a/SlipeServer.Packets/SyncStructures/CameraOrientationStructure.cs +++ b/SlipeServer.Packets/SyncStructures/CameraOrientationStructure.cs @@ -60,6 +60,14 @@ public void Read(PacketReader reader) public void Write(PacketBuilder builder) { - + builder.WriteFloatFromBits(this.CameraForward.Z, 8, -MathF.PI, MathF.PI, false); + builder.WriteFloatFromBits(this.CameraForward.X, 8, -MathF.PI, MathF.PI, false); + builder.Write(true); + builder.WriteCapped(3, 2); + uint bitCount = bitCountLookup[3]; + float range = MathF.Pow(2, bitCount); + builder.WriteFloatFromBits(this.CameraForward.X, (int)bitCount, -range, range, false); + builder.WriteFloatFromBits(this.CameraForward.Y, (int)bitCount, -range, range, false); + builder.WriteFloatFromBits(this.CameraForward.Z, (int)bitCount, -range, range, false); } } diff --git a/SlipeServer.Server.TestTools/TestingPlayer.cs b/SlipeServer.Server.TestTools/TestingPlayer.cs index f78a8543..6117c2f0 100644 --- a/SlipeServer.Server.TestTools/TestingPlayer.cs +++ b/SlipeServer.Server.TestTools/TestingPlayer.cs @@ -1,7 +1,10 @@ using Moq; using SlipeServer.Net.Wrappers; +using SlipeServer.Packets; using SlipeServer.Packets.Definitions.Lua.ElementRpc.Element; +using SlipeServer.Packets.Definitions.Sync; using SlipeServer.Server.Elements; +using SlipeServer.Server.Elements.Events; using System; using System.Numerics; @@ -18,30 +21,82 @@ public static Player CreateStandalone() } } -public class ClientEnvironment +public enum EventSide +{ + Client, + Server +} + +public class ClientPlayer { - public TestingPlayer Player { get; } + public ClientEnvironment ClientEnvironment { get; } + public event Action? PositionChanged; public Vector3 Position { get; private set; } - public ClientEnvironment(TestingPlayer testingPlayer) + public ClientPlayer(ClientEnvironment clientEnvironment) + { + this.ClientEnvironment = clientEnvironment; + } + + public void SetPosition(Vector3 position, EventSide eventSide = EventSide.Client) { - this.Player = testingPlayer; - var testingClient = testingPlayer.Client as TestingClient; - testingClient.PacketSent += HandlePacketSent; + this.Position = position; + this.PositionChanged?.Invoke(this, position, eventSide); } - private void HandlePacketSent(TestingClient client, Packets.Packet packet) + /// + /// This is reimplementation of this function: void CNetAPI::DoPulse() + /// + public void SynchronizeWithServer() + { + var packet = new PlayerPureSyncPacket + { + Position = this.Position, + }; + + this.ClientEnvironment.Client.SendPacket(packet); + } +} + +public class ClientEnvironment +{ + public TestingServer TestingServer { get; } + public TestingPlayer ServerPlayer { get; } + public ClientPlayer ClientPlayer { get; } + public TestingClient Client { get; } + public TimeContext TimeContext { get; } + + public ClientEnvironment(TestingServer testingServer, TestingPlayer testingPlayer) + { + this.TestingServer = testingServer; + this.ServerPlayer = testingPlayer; + this.ClientPlayer = new ClientPlayer(this); + this.Client = testingPlayer.Client as TestingClient; + this.TimeContext = new(); + this.Client.PacketSent += HandlePacketSent; + this.ServerPlayer.Disconnected += HandleDisconnected; + //TestingServer.NetWrapperMock + } + + private void HandleDisconnected(Player sender, PlayerQuitEventArgs e) + { + if(sender.Client is TestingClient testingClient) + testingClient.PacketSent -= HandlePacketSent; + } + + private void HandlePacketSent(TestingClient client, Packet packet) { switch (packet) { case SetElementPositionRpcPacket setElementPositionRpcPacket: - if (setElementPositionRpcPacket.ElementId == this.Player.Id) + if (setElementPositionRpcPacket.ElementId == this.ServerPlayer.Id) { - this.Position = setElementPositionRpcPacket.Position; - } else + this.ClientPlayer.SetPosition(setElementPositionRpcPacket.Position, EventSide.Server); + } + else { - throw new Exception("Element not found."); + throw new InvalidOperationException($"Element with ID {setElementPositionRpcPacket.ElementId} does not match Player ID {this.ServerPlayer.Id}. Ensure the correct packet is being sent."); } break; } diff --git a/SlipeServer.Server.TestTools/TestingServer.cs b/SlipeServer.Server.TestTools/TestingServer.cs index 8aaa4dc1..f50a7847 100644 --- a/SlipeServer.Server.TestTools/TestingServer.cs +++ b/SlipeServer.Server.TestTools/TestingServer.cs @@ -76,6 +76,7 @@ private void SetupSendPacketMocks() this.NetWrapperMock.Setup(x => x.SendPacket(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((ulong address, ushort version, Packet packet) => { + var data = packet.Write(); this.sendPacketCalls.Add(new SendPacketCall() { Address = address, @@ -85,6 +86,7 @@ private void SetupSendPacketMocks() Priority = packet.Priority, Reliability = packet.Reliability }); + this.EnqueueIncomingPacket(this.NetWrapperMock.Object, address, packet.PacketId, data, null); }); this.NetWrapperMock.Setup(x => x.SendPacket( diff --git a/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs b/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs index 8c914f27..aee452a6 100644 --- a/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs +++ b/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs @@ -1,5 +1,10 @@ using FluentAssertions; +using FluentAssertions.Execution; +using SlipeServer.Server.Elements; +using SlipeServer.Server.Elements.Events; using SlipeServer.Server.TestTools; +using System.Numerics; +using System.Threading; using Xunit; namespace SlipeServer.Server.Tests.Integration; @@ -10,12 +15,28 @@ public class ClientEnvironmentTests public void SettingGettingPositionShouldWork() { var server = new TestingServer(); - + server.AddDefaultPacketHandlers(); var player = server.AddFakePlayer(); - var clientInterface = new ClientEnvironment(player); + var clientEnvironment = new ClientEnvironment(server, player); + + using var monitor = clientEnvironment.ClientPlayer.Monitor(); + player.Position = new Vector3(3, 3, 3); + var clientPositionWas = player.Position; + clientEnvironment.ClientPlayer.SetPosition(new Vector3(5, 0, 0)); + + var waitHandle = new AutoResetEvent(false); + void handlePositionChanged(Element sender, ElementChangedEventArgs args) + { + waitHandle.Set(); + } + player.PositionChanged += handlePositionChanged; + clientEnvironment.ClientPlayer.SynchronizeWithServer(); + + waitHandle.WaitOne(1000); - player.Position = new System.Numerics.Vector3(3, 3, 3); + using var _ = new AssertionScope(); - clientInterface.Position.Should().Be(player.Position); + clientPositionWas.Should().Be(new Vector3(3, 3, 3)); + player.Position.Should().Be(new Vector3(5, 0, 0)); } } diff --git a/SlipeServer.Server.Tests/Unit/Elements/ElementTests.cs b/SlipeServer.Server.Tests/Unit/Elements/ElementTests.cs index 3872d9ba..415712bf 100644 --- a/SlipeServer.Server.Tests/Unit/Elements/ElementTests.cs +++ b/SlipeServer.Server.Tests/Unit/Elements/ElementTests.cs @@ -13,7 +13,7 @@ public void GetAndIncrementTimeContext_ReturnsNewTimeContext() var element = new Element(); var context = element.TimeContext; - var incrementContext = element.GetAndIncrementTimeContext(); + var incrementContext = element.TimeContext.GetAndIncrement(); context.Should().NotBe(incrementContext); } @@ -25,7 +25,7 @@ public void GetAndIncrementTimeContext_WrapsAroundSkippingZero() for (int i = 0; i < 255; i++) { - element.GetAndIncrementTimeContext(); + element.TimeContext.GetAndIncrement(); } var context = element.TimeContext; diff --git a/SlipeServer.Server/ElementConcepts/Camera.cs b/SlipeServer.Server/ElementConcepts/Camera.cs index 11238223..f617501c 100644 --- a/SlipeServer.Server/ElementConcepts/Camera.cs +++ b/SlipeServer.Server/ElementConcepts/Camera.cs @@ -87,6 +87,6 @@ public void SetMatrix(Vector3 position, Vector3 lookAt, float roll = 0, float fo this.target = null; this.Position = position; this.LookAt = lookAt; - this.player.Client.SendPacket(new SetCameraMatrixPacket(position, lookAt, roll, fov, this.player.GetAndIncrementTimeContext())); + this.player.Client.SendPacket(new SetCameraMatrixPacket(position, lookAt, roll, fov, this.player.TimeContext.GetAndIncrement())); } } diff --git a/SlipeServer.Server/Elements/Element.cs b/SlipeServer.Server/Elements/Element.cs index 6ea36479..6e840249 100644 --- a/SlipeServer.Server/Elements/Element.cs +++ b/SlipeServer.Server/Elements/Element.cs @@ -75,12 +75,7 @@ public ElementId Id } } - private readonly object timeContextLock = new(); - /// - /// The time sync context, this is a value used to verify whether synchronisation packets are to be applied or ignored. - /// - public byte TimeContext { get; private set; } - + public TimeContext TimeContext { get; } private string name = ""; /// @@ -386,7 +381,7 @@ public Element() this.associatedPlayers = []; this.subscribers = []; this.attachedElements = []; - this.TimeContext = 1; + this.TimeContext = new(); this.ElementData = []; this.ElementDataSubscriptions = new(); @@ -425,22 +420,6 @@ public void RemoveSubscriber(Player player) player.UnsubscribeFrom(this); } - /// - /// Returns a new time context, to be used when sync updates sent prior to this moment are meant to be invaldiated. - /// - /// The new time context - public byte GetAndIncrementTimeContext() - { - lock (this.timeContextLock) - { - if (++this.TimeContext == 0) - { - this.TimeContext++; - } - return this.TimeContext; - } - } - /// /// Indicates whether a specific time context is valid to update this element /// diff --git a/SlipeServer.Server/Elements/TimeContext.cs b/SlipeServer.Server/Elements/TimeContext.cs new file mode 100644 index 00000000..830c79a9 --- /dev/null +++ b/SlipeServer.Server/Elements/TimeContext.cs @@ -0,0 +1,48 @@ +namespace SlipeServer.Server.Elements; + +public struct TimeContext +{ + private readonly object @lock; + private byte current; + + public TimeContext() + { + this.current = 1; + this.@lock = new(); + } + + /// + /// The current time context value, used to verify whether synchronization packets are to be applied or ignored. + /// + public byte Current + { + get + { + lock (this.@lock) + { + return this.current; + } + } + } + + /// + /// Generates and returns a new time context. This can be used to invalidate synchronization updates sent prior to this moment. + /// + /// The new time context value. + public byte GetAndIncrement() + { + lock (this.@lock) + { + if (++this.current == 0) + { + this.current++; + } + return this.current; + } + } + + public static implicit operator byte(TimeContext manager) + { + return manager.Current; + } +} diff --git a/SlipeServer.Server/Extensions/Relaying/PedPropertyRelayingExtensions.cs b/SlipeServer.Server/Extensions/Relaying/PedPropertyRelayingExtensions.cs index 72122681..9fab18fd 100644 --- a/SlipeServer.Server/Extensions/Relaying/PedPropertyRelayingExtensions.cs +++ b/SlipeServer.Server/Extensions/Relaying/PedPropertyRelayingExtensions.cs @@ -39,7 +39,7 @@ public static void AddPedRelayers(this Ped ped) private static void RelayPedWasted(Ped sender, PedWastedEventArgs e) { var packet = new PedWastedPacket( - e.Source.Id, e.Killer?.Id ?? ElementId.Zero, (byte)e.WeaponType, (byte)e.BodyPart, e.Ammo, false, sender.GetAndIncrementTimeContext(), e.AnimationGroup, e.AnimationId + e.Source.Id, e.Killer?.Id ?? ElementId.Zero, (byte)e.WeaponType, (byte)e.BodyPart, e.Ammo, false, sender.TimeContext.GetAndIncrement(), e.AnimationGroup, e.AnimationId ) { Ammo = e.Ammo diff --git a/SlipeServer.Server/Extensions/Relaying/VehiclePropertyRelayingExtensions.cs b/SlipeServer.Server/Extensions/Relaying/VehiclePropertyRelayingExtensions.cs index fe7588ef..219b1310 100644 --- a/SlipeServer.Server/Extensions/Relaying/VehiclePropertyRelayingExtensions.cs +++ b/SlipeServer.Server/Extensions/Relaying/VehiclePropertyRelayingExtensions.cs @@ -262,7 +262,7 @@ private static void RelayAreSirensOn(Vehicle sender, ElementChangedEventArgs args) { if (!args.IsSync) - sender.RelayChange(new SetElementHealthRpcPacket(sender.Id, sender.GetAndIncrementTimeContext(), args.NewValue)); + sender.RelayChange(new SetElementHealthRpcPacket(sender.Id, sender.TimeContext.GetAndIncrement(), args.NewValue)); } private static void RelayRespawn(Element sender, VehicleRespawnEventArgs args) @@ -270,7 +270,7 @@ private static void RelayRespawn(Element sender, VehicleRespawnEventArgs args) sender.RelayChange(new VehicleSpawnPacket(new VehicleSpawnInfo[] { new VehicleSpawnInfo { ElementId = args.Vehicle.Id, - TimeContext = args.Vehicle.GetAndIncrementTimeContext(), + TimeContext = args.Vehicle.TimeContext.GetAndIncrement(), VehicleId = args.Vehicle.Model, Position = args.Vehicle.RespawnPosition, Rotation = args.Vehicle.RespawnRotation, @@ -286,7 +286,7 @@ private static void RelayEnter(Element sender, VehicleEnteredEventsArgs eventArg eventArgs.Ped.Id, eventArgs.Vehicle.Id, eventArgs.Seat, - eventArgs.Ped.GetAndIncrementTimeContext() + eventArgs.Ped.TimeContext.GetAndIncrement() )); } } @@ -297,7 +297,7 @@ private static void RelayeLeft(Element sender, VehicleLeftEventArgs eventArgs) { sender.RelayChange(new RemoveFromVehiclePacket( eventArgs.Ped.Id, - eventArgs.Ped.GetAndIncrementTimeContext() + eventArgs.Ped.TimeContext.GetAndIncrement() )); } } diff --git a/SlipeServer.Server/MtaServer.cs b/SlipeServer.Server/MtaServer.cs index 99f7552d..a946e854 100644 --- a/SlipeServer.Server/MtaServer.cs +++ b/SlipeServer.Server/MtaServer.cs @@ -557,7 +557,7 @@ public void RegisterNetWrapper(INetWrapper netWrapper) this.clients[netWrapper] = new(); } - private void EnqueueIncomingPacket(INetWrapper netWrapper, ulong binaryAddress, PacketId packetId, byte[] data, uint? ping) + protected void EnqueueIncomingPacket(INetWrapper netWrapper, ulong binaryAddress, PacketId packetId, byte[] data, uint? ping) { if (!this.clients[netWrapper].ContainsKey(binaryAddress)) { diff --git a/SlipeServer.Server/PacketHandling/Factories/ElementPacketFactory.cs b/SlipeServer.Server/PacketHandling/Factories/ElementPacketFactory.cs index e3f813a2..ed0ffdf2 100644 --- a/SlipeServer.Server/PacketHandling/Factories/ElementPacketFactory.cs +++ b/SlipeServer.Server/PacketHandling/Factories/ElementPacketFactory.cs @@ -11,15 +11,15 @@ public static class ElementPacketFactory { public static SetElementPositionRpcPacket CreateSetPositionPacket(Element element, Vector3 position, bool isWarp = false) { - return new SetElementPositionRpcPacket(element.Id, element.GetAndIncrementTimeContext(), position, isWarp); + return new SetElementPositionRpcPacket(element.Id, element.TimeContext.GetAndIncrement(), position, isWarp); } public static Packet CreateSetRotationPacket(Element element, Vector3 rotation) { return element switch { - Vehicle => new SetVehicleRotationRpcPacket(element.Id, element.GetAndIncrementTimeContext(), rotation), - Ped => new SetPedRotationRpcPacket(element.Id, element.GetAndIncrementTimeContext(), rotation.Z / (180 / MathF.PI), true), + Vehicle => new SetVehicleRotationRpcPacket(element.Id, element.TimeContext.GetAndIncrement(), rotation), + Ped => new SetPedRotationRpcPacket(element.Id, element.TimeContext.GetAndIncrement(), rotation.Z / (180 / MathF.PI), true), WorldObject => new SetObjectRotationRpcPacket(element.Id, rotation), _ => throw new NotImplementedException($"Can not create set rotation packet for {element.GetType()}"), }; @@ -27,12 +27,12 @@ public static Packet CreateSetRotationPacket(Element element, Vector3 rotation) public static FixVehicleRpcPacket CreateFixVehiclePacket(Element element) { - return new FixVehicleRpcPacket(element.Id, element.GetAndIncrementTimeContext()); + return new FixVehicleRpcPacket(element.Id, element.TimeContext.GetAndIncrement()); } public static SetElementHealthRpcPacket CreateSetHealthPacket(Element element, float health) { - return new SetElementHealthRpcPacket(element.Id, element.GetAndIncrementTimeContext(), health); + return new SetElementHealthRpcPacket(element.Id, element.TimeContext.GetAndIncrement(), health); } public static SetElementAlphaRpcPacket CreateSetAlphaPacket(Element element, byte alpha) diff --git a/SlipeServer.Server/PacketHandling/Factories/PedPacketFactory.cs b/SlipeServer.Server/PacketHandling/Factories/PedPacketFactory.cs index e21ea8b8..c984ec34 100644 --- a/SlipeServer.Server/PacketHandling/Factories/PedPacketFactory.cs +++ b/SlipeServer.Server/PacketHandling/Factories/PedPacketFactory.cs @@ -20,12 +20,12 @@ public static SetElementModelRpcPacket CreateSetModelPacket(Ped ped) public static SetElementHealthRpcPacket CreateSetHealthPacket(Ped ped) { - return new SetElementHealthRpcPacket(ped.Id, ped.GetAndIncrementTimeContext(), ped.Health); + return new SetElementHealthRpcPacket(ped.Id, ped.TimeContext.GetAndIncrement(), ped.Health); } public static SetPedArmourRpcPacket CreateSetArmourPacket(Ped ped) { - return new SetPedArmourRpcPacket(ped.Id, ped.GetAndIncrementTimeContext(), ped.Armor); + return new SetPedArmourRpcPacket(ped.Id, ped.TimeContext.GetAndIncrement(), ped.Armor); } public static GivePedJetpackRpcPacket CreateGiveJetpack(Ped ped) diff --git a/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs b/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs index 753f61e4..adb6a8bf 100644 --- a/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs +++ b/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs @@ -125,7 +125,7 @@ public static PlayerQuitPacket CreateQuitPacket(Player player, QuitReason reason public static SpawnPlayerPacket CreateSpawnPacket(Player player) { - return new SpawnPlayerPacket(player.Id, 0, player.Position, player.PedRotation, player.Model, ElementId.Zero, player.Interior, player.Dimension, player.GetAndIncrementTimeContext()); + return new SpawnPlayerPacket(player.Id, 0, player.Position, player.PedRotation, player.Model, ElementId.Zero, player.Interior, player.Dimension, player.TimeContext.GetAndIncrement()); } public static PlayerWastedPacket CreateWastedPacket( @@ -134,7 +134,7 @@ public static PlayerWastedPacket CreateWastedPacket( ) { return new PlayerWastedPacket(player.Id, killer?.Id ?? ElementId.Zero, (byte)weaponType, (byte)bodyPart, isStealth, - player.GetAndIncrementTimeContext(), animationGroup, animationId); + player.TimeContext.GetAndIncrement(), animationGroup, animationId); } public static ChangeNicknamePacket CreateNicknameChangePacket(Player player) diff --git a/SlipeServer.Server/PacketHandling/Factories/VehiclePacketFactory.cs b/SlipeServer.Server/PacketHandling/Factories/VehiclePacketFactory.cs index afe348f1..b47d1b2a 100644 --- a/SlipeServer.Server/PacketHandling/Factories/VehiclePacketFactory.cs +++ b/SlipeServer.Server/PacketHandling/Factories/VehiclePacketFactory.cs @@ -107,11 +107,11 @@ public static DestroyAllVehiclesRpcPacket CreateDestroyAllPacket() public static FixVehicleRpcPacket CreateFixVehiclePacket(Vehicle vehicle) { - return new FixVehicleRpcPacket(vehicle.Id, vehicle.GetAndIncrementTimeContext()); + return new FixVehicleRpcPacket(vehicle.Id, vehicle.TimeContext.GetAndIncrement()); } public static VehicleBlownRpcPacket CreateBlownVehiclePacket(Vehicle vehicle, bool createExplosion) { - return new VehicleBlownRpcPacket(vehicle.Id, vehicle.GetAndIncrementTimeContext(), createExplosion); + return new VehicleBlownRpcPacket(vehicle.Id, vehicle.TimeContext.GetAndIncrement(), createExplosion); } } From b85003d7f00236ae1e1ad787fa96fe86039aa8bb Mon Sep 17 00:00:00 2001 From: Sebastian Jura <22455534+CrosRoad95@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:28:35 +0100 Subject: [PATCH 3/4] Add test packet queue handler --- .../TestPacketQueueHandler.cs | 54 +++++++++++++++++++ SlipeServer.Server.TestTools/TestingServer.cs | 14 +++++ .../Integration/ClientEnvironmentTests.cs | 9 +--- SlipeServer.Server/MtaServer.cs | 2 +- 4 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 SlipeServer.Server.TestTools/TestPacketQueueHandler.cs diff --git a/SlipeServer.Server.TestTools/TestPacketQueueHandler.cs b/SlipeServer.Server.TestTools/TestPacketQueueHandler.cs new file mode 100644 index 00000000..94edc8cf --- /dev/null +++ b/SlipeServer.Server.TestTools/TestPacketQueueHandler.cs @@ -0,0 +1,54 @@ +using SlipeServer.Packets; +using SlipeServer.Server.PacketHandling.Handlers.QueueHandlers; +using SlipeServer.Server.PacketHandling.Handlers; +using SlipeServer.Server.PacketHandling; +using System; + +namespace SlipeServer.Server.TestTools; + +public class TestPacketQueueHandlerDispatcher +{ + public event Action? Flushed; + + public void Flush() + { + Flushed?.Invoke(); + } +} + +public class TestPacketQueueHandler : BasePacketQueueHandler where T : Packet +{ + private readonly TestPacketQueueHandlerDispatcher testPacketQueueHandlerDispatcher; + private readonly IPacketHandler packetHandler; + + public TestPacketQueueHandler(TestPacketQueueHandlerDispatcher testPacketQueueHandlerDispatcher, IPacketHandler packetHandler) + { + this.testPacketQueueHandlerDispatcher = testPacketQueueHandlerDispatcher; + this.packetHandler = packetHandler; + + testPacketQueueHandlerDispatcher.Flushed += HandleFlushed; + } + + private void HandleFlushed() + { + while (this.packetQueue.TryDequeue(out var queueEntry)) + { + try + { + ClientContext.Current = queueEntry.Client; + this.packetHandler.HandlePacket(queueEntry.Client, queueEntry.Packet); + TriggerPacketHandled(queueEntry.Packet); + } + finally + { + ClientContext.Current = null; + } + } + } + + public override void Dispose() + { + testPacketQueueHandlerDispatcher.Flushed -= HandleFlushed; + base.Dispose(); + } +} diff --git a/SlipeServer.Server.TestTools/TestingServer.cs b/SlipeServer.Server.TestTools/TestingServer.cs index f50a7847..94c0e1d7 100644 --- a/SlipeServer.Server.TestTools/TestingServer.cs +++ b/SlipeServer.Server.TestTools/TestingServer.cs @@ -11,12 +11,15 @@ using SlipeServer.Packets.Lua.Event; using SlipeServer.Server.Clients; using SlipeServer.Server.Elements; +using SlipeServer.Server.PacketHandling.Handlers.QueueHandlers; +using SlipeServer.Server.PacketHandling.Handlers; using SlipeServer.Server.Resources.Serving; using SlipeServer.Server.ServerBuilders; using System; using System.Collections.Generic; using System.Linq; using ILogger = Microsoft.Extensions.Logging.ILogger; +using System.Diagnostics.Tracing; namespace SlipeServer.Server.TestTools; @@ -113,11 +116,17 @@ private void SetupSendPacketMocks() public static void ConfigureOverrides(IServiceCollection services) { var httpServerMock = new Mock(); + services.AddSingleton(); services.AddSingleton(httpServerMock.Object); services.AddLogging(); services.AddSingleton(x => x.GetRequiredService>()); } + public void FlushPacketQueueHandler() + { + GetRequiredService().Flush(); + } + public TPlayer AddFakePlayer() { var address = ++this.binaryAddressCounter; @@ -214,6 +223,11 @@ public override void Start() this.IsRunning = true; } + public override void RegisterPacketHandler(params object[] parameters) + { + RegisterPacketHandler, TPacketHandler>(); + } + public override void Stop() { Stopped?.Invoke(); diff --git a/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs b/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs index aee452a6..3fcbe3c6 100644 --- a/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs +++ b/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs @@ -24,15 +24,8 @@ public void SettingGettingPositionShouldWork() var clientPositionWas = player.Position; clientEnvironment.ClientPlayer.SetPosition(new Vector3(5, 0, 0)); - var waitHandle = new AutoResetEvent(false); - void handlePositionChanged(Element sender, ElementChangedEventArgs args) - { - waitHandle.Set(); - } - player.PositionChanged += handlePositionChanged; clientEnvironment.ClientPlayer.SynchronizeWithServer(); - - waitHandle.WaitOne(1000); + server.FlushPacketQueueHandler(); using var _ = new AssertionScope(); diff --git a/SlipeServer.Server/MtaServer.cs b/SlipeServer.Server/MtaServer.cs index a946e854..b5d8987c 100644 --- a/SlipeServer.Server/MtaServer.cs +++ b/SlipeServer.Server/MtaServer.cs @@ -301,7 +301,7 @@ public void RegisterPacketHandler( /// /// /// - public void RegisterPacketHandler(params object[] parameters) + public virtual void RegisterPacketHandler(params object[] parameters) where TPacket : Packet, new() where TPacketHandler : IPacketHandler { From 7228a1031edc015683956fed44bf4a10f82c3814 Mon Sep 17 00:00:00 2001 From: Sebastian Jura <22455534+CrosRoad95@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:52:43 +0100 Subject: [PATCH 4/4] Next --- .../Definitions/Entities/AddEntityPacket.cs | 12 +- .../Definitions/Join/PlayerListPacket.cs | 61 ++++++-- .../Player/ChangeNicknamePacket.cs | 14 +- .../Definitions/Rpc/RpcPacket.cs | 11 +- SlipeServer.Packets/Reader/PacketReader.cs | 9 +- SlipeServer.Server.TestTools/TestingClient.cs | 29 ++-- SlipeServer.Server.TestTools/TestingPlayer.cs | 136 ++++++++++++++---- SlipeServer.Server.TestTools/TestingServer.cs | 8 ++ .../Integration/ClientEnvironmentTests.cs | 35 ----- .../Integration/ClientPlayerTests.cs | 37 +++++ .../Factories/PlayerPacketFactory.cs | 11 +- 11 files changed, 264 insertions(+), 99 deletions(-) delete mode 100644 SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs create mode 100644 SlipeServer.Server.Tests/Integration/ClientPlayerTests.cs diff --git a/SlipeServer.Packets/Definitions/Entities/AddEntityPacket.cs b/SlipeServer.Packets/Definitions/Entities/AddEntityPacket.cs index 2bb00d72..8baff05f 100644 --- a/SlipeServer.Packets/Definitions/Entities/AddEntityPacket.cs +++ b/SlipeServer.Packets/Definitions/Entities/AddEntityPacket.cs @@ -1,6 +1,7 @@ using SlipeServer.Packets.Builder; using SlipeServer.Packets.Definitions.Entities.Structs; using SlipeServer.Packets.Enums; +using SlipeServer.Packets.Reader; using SlipeServer.Packets.Structs; using System; using System.Drawing; @@ -18,6 +19,8 @@ public class AddEntityPacket : Packet private readonly PacketBuilder builder; private uint entityCount; + public bool HasAnyEntity => entityCount > 0; + public AddEntityPacket() { this.builder = new PacketBuilder(); @@ -785,7 +788,14 @@ public void AddWater( public override void Read(byte[] bytes) { - throw new NotSupportedException(); + var reader = new PacketReader(bytes); + this.entityCount = reader.GetCompressedUInt32(); + + for (int i = 0; i < entityCount; i++) + { + + } + ; } public override byte[] Write() diff --git a/SlipeServer.Packets/Definitions/Join/PlayerListPacket.cs b/SlipeServer.Packets/Definitions/Join/PlayerListPacket.cs index 0664bbdb..235e5eea 100644 --- a/SlipeServer.Packets/Definitions/Join/PlayerListPacket.cs +++ b/SlipeServer.Packets/Definitions/Join/PlayerListPacket.cs @@ -1,27 +1,58 @@ using SlipeServer.Packets.Builder; using SlipeServer.Packets.Enums; +using SlipeServer.Packets.Reader; using SlipeServer.Packets.Structs; using System; +using System.Collections.Generic; using System.Drawing; using System.Numerics; namespace SlipeServer.Packets.Definitions.Join; +public record PacketPlayer(ElementId playerId, + byte timeContext, + string nickname, + ushort bitsreamVersion, + uint buildNumber, + + bool isDead, + bool isInVehicle, + bool hasJetpack, + bool isNametagShowing, + bool isHeadless, + bool isFrozen, + + string nametagText, + Color? nametagColor, + byte moveAnimation, + + ushort model, + ElementId? teamId, + + ElementId? vehicleId, + byte? seat, + + Vector3? position, + float? rotation, + + ushort dimension, + byte fightingStyle, + byte alpha, + byte interior, + + byte[] weapons); + public class PlayerListPacket : Packet { public override PacketId PacketId => PacketId.PACKET_ID_PLAYER_LIST; public override PacketReliability Reliability => PacketReliability.ReliableSequenced; public override PacketPriority Priority => PacketPriority.High; + private PacketBuilder builder = new(); + public List? Players { get; private set; } - private readonly PacketBuilder builder; - - public PlayerListPacket(bool showInChat) - { - this.builder = new PacketBuilder(); - - this.builder.Write(showInChat); - } + public bool ShowInChat { get; set; } + public PlayerListPacket() { } public void AddPlayer( ElementId playerId, @@ -129,11 +160,21 @@ byte[] weapons public override void Read(byte[] bytes) { - throw new NotSupportedException(); + this.Players = new(); + var reader = new PacketReader(bytes); + this.ShowInChat = reader.GetBit(); + while (!reader.IsFinishedReading) + { + var playerId = reader.GetElementId(); + var timeContext = reader.GetByte(); + var nickname = reader.GetStringWithByteAsLength(); + ; + + } } public override byte[] Write() { - return this.builder.Build(); + return this.builder!.Build(); } } diff --git a/SlipeServer.Packets/Definitions/Player/ChangeNicknamePacket.cs b/SlipeServer.Packets/Definitions/Player/ChangeNicknamePacket.cs index e2e24bf4..aafe34a8 100644 --- a/SlipeServer.Packets/Definitions/Player/ChangeNicknamePacket.cs +++ b/SlipeServer.Packets/Definitions/Player/ChangeNicknamePacket.cs @@ -1,5 +1,6 @@ using SlipeServer.Packets.Builder; using SlipeServer.Packets.Enums; +using SlipeServer.Packets.Reader; using SlipeServer.Packets.Structs; using System; @@ -14,18 +15,11 @@ public class ChangeNicknamePacket : Packet public ElementId PlayerId { get; set; } public string Name { get; set; } - public ChangeNicknamePacket( - ElementId playerId, - string name - ) - { - this.PlayerId = playerId; - this.Name = name; - } - public override void Read(byte[] bytes) { - throw new NotSupportedException(); + var reader = new PacketReader(bytes); + this.PlayerId = reader.GetElementId(); + this.Name = reader.GetStringCharacters(reader.RemainingBytes); } public override byte[] Write() diff --git a/SlipeServer.Packets/Definitions/Rpc/RpcPacket.cs b/SlipeServer.Packets/Definitions/Rpc/RpcPacket.cs index 08ae4235..c47ade49 100644 --- a/SlipeServer.Packets/Definitions/Rpc/RpcPacket.cs +++ b/SlipeServer.Packets/Definitions/Rpc/RpcPacket.cs @@ -1,4 +1,5 @@ -using SlipeServer.Packets.Enums; +using SlipeServer.Packets.Builder; +using SlipeServer.Packets.Enums; using SlipeServer.Packets.Reader; using System; @@ -10,7 +11,7 @@ public class RpcPacket : Packet public override PacketReliability Reliability => PacketReliability.ReliableSequenced; public override PacketPriority Priority => PacketPriority.High; - public RpcFunctions FunctionId { get; private set; } + public RpcFunctions FunctionId { get; set; } public PacketReader Reader { get; private set; } @@ -28,6 +29,10 @@ public override void Read(byte[] bytes) public override byte[] Write() { - throw new NotSupportedException(); + var builder = new PacketBuilder(); + + builder.Write((byte)this.FunctionId); + + return builder.Build(); } } diff --git a/SlipeServer.Packets/Reader/PacketReader.cs b/SlipeServer.Packets/Reader/PacketReader.cs index 8dd8fd76..e87a3c8e 100644 --- a/SlipeServer.Packets/Reader/PacketReader.cs +++ b/SlipeServer.Packets/Reader/PacketReader.cs @@ -15,6 +15,7 @@ public class PacketReader public int Counter { get; private set; } public int Size { get; private set; } public bool IsFinishedReading => this.Counter == this.Size; + public int RemainingBytes => (this.Size - this.Counter) / 8; public PacketReader(byte[] data) { @@ -146,7 +147,13 @@ public string GetString() int length = GetUint16(); return GetStringCharacters(length); } - + + public string GetStringWithByteAsLength() + { + var length = GetByte(); + return GetStringCharacters(length); + } + public ElementId GetElementId() { var id = BitConverter.ToUInt32(GetBytesCapped(17).Concat(new byte[] { 0 }).ToArray(), 0); diff --git a/SlipeServer.Server.TestTools/TestingClient.cs b/SlipeServer.Server.TestTools/TestingClient.cs index 19400b16..3788a8ca 100644 --- a/SlipeServer.Server.TestTools/TestingClient.cs +++ b/SlipeServer.Server.TestTools/TestingClient.cs @@ -1,7 +1,9 @@ using SlipeServer.Net.Wrappers; using SlipeServer.Packets; +using SlipeServer.Packets.Definitions.Join; using SlipeServer.Packets.Definitions.Lua.ElementRpc; using SlipeServer.Packets.Definitions.Lua.ElementRpc.Element; +using SlipeServer.Packets.Definitions.Player; using SlipeServer.Packets.Enums; using SlipeServer.Server.Clients; using SlipeServer.Server.Elements; @@ -32,6 +34,7 @@ public override void SendPacket(Packet packet) public override void SendPacket(PacketId packetId, byte[] data, PacketPriority priority = PacketPriority.Medium, PacketReliability reliability = PacketReliability.Unreliable) { base.SendPacket(packetId, data, priority, reliability); + Packet? packet = null; switch (packetId) { case PacketId.PACKET_ID_LUA_ELEMENT_RPC: @@ -39,18 +42,28 @@ public override void SendPacket(PacketId packetId, byte[] data, PacketPriority p switch (elementRpcFunction) { case ElementRpcFunction.SET_ELEMENT_POSITION: - HandleElementSetElementPositionPacket(data); + packet = new SetElementPositionRpcPacket(); break; } - ; break; + case PacketId.PACKET_ID_ENTITY_ADD: + packet = new AddEntityPacket(); + break; + case PacketId.PACKET_ID_PLAYER_LIST: + packet = new PlayerListPacket(); + break; + case PacketId.PACKET_ID_PLAYER_CHANGE_NICK: + packet = new ChangeNicknamePacket(); + break; + default: + throw new NotImplementedException(packetId.ToString()); + } - } - private void HandleElementSetElementPositionPacket(byte[] data) - { - var packet = new SetElementPositionRpcPacket(); - packet.Read(data); - PacketSent?.Invoke(this, packet); + if(packet != null) + { + packet.Read(data); + PacketSent?.Invoke(this, packet); + } } } diff --git a/SlipeServer.Server.TestTools/TestingPlayer.cs b/SlipeServer.Server.TestTools/TestingPlayer.cs index 6117c2f0..2c91089f 100644 --- a/SlipeServer.Server.TestTools/TestingPlayer.cs +++ b/SlipeServer.Server.TestTools/TestingPlayer.cs @@ -1,11 +1,18 @@ using Moq; using SlipeServer.Net.Wrappers; using SlipeServer.Packets; +using SlipeServer.Packets.Definitions.Join; using SlipeServer.Packets.Definitions.Lua.ElementRpc.Element; +using SlipeServer.Packets.Definitions.Player; using SlipeServer.Packets.Definitions.Sync; +using SlipeServer.Packets.Rpc; +using SlipeServer.Packets.Structs; +using SlipeServer.Server.ElementCollections; using SlipeServer.Server.Elements; using SlipeServer.Server.Elements.Events; +using SlipeServer.Server.PacketHandling.Handlers.Rpc; using System; +using System.Collections.Generic; using System.Numerics; namespace SlipeServer.Server.TestTools; @@ -27,16 +34,52 @@ public enum EventSide Server } -public class ClientPlayer +public class ClientElementCollection { - public ClientEnvironment ClientEnvironment { get; } + private readonly object @lock = new(); + private readonly Dictionary elements = []; - public event Action? PositionChanged; + public void Add(ClientElement clientElement) + { + lock (@lock) + { + this.elements.TryAdd(clientElement.ElementId, clientElement); + } + } + + public void Remove(ClientElement clientElement) + { + lock (@lock) + { + this.elements.Remove(clientElement.ElementId); + } + } + + public ClientElement Get(ElementId elementId) + { + lock (@lock) + { + if (this.elements.TryGetValue(elementId, out var clientElement)) + return clientElement; + throw new KeyNotFoundException($"Element of id {elementId} not found."); + } + } +} + +public class ClientElement +{ + public TimeContext TimeContext { get; } = new(); + public ElementId ElementId { get; } + + + public event Action? PositionChanged; public Vector3 Position { get; private set; } + public event Action? NameChanged; + public string Name { get; private set; } - public ClientPlayer(ClientEnvironment clientEnvironment) + public ClientElement(ElementId elementId) { - this.ClientEnvironment = clientEnvironment; + this.ElementId = elementId; } public void SetPosition(Vector3 position, EventSide eventSide = EventSide.Client) @@ -45,43 +88,39 @@ public void SetPosition(Vector3 position, EventSide eventSide = EventSide.Client this.PositionChanged?.Invoke(this, position, eventSide); } - /// - /// This is reimplementation of this function: void CNetAPI::DoPulse() - /// - public void SynchronizeWithServer() + public void SetName(string name, EventSide eventSide = EventSide.Client) { - var packet = new PlayerPureSyncPacket - { - Position = this.Position, - }; - - this.ClientEnvironment.Client.SendPacket(packet); + this.Name = name; + this.NameChanged?.Invoke(this, name, eventSide); } } -public class ClientEnvironment +public class ClientPlayer : ClientElement where TPlayer : Player { - public TestingServer TestingServer { get; } - public TestingPlayer ServerPlayer { get; } - public ClientPlayer ClientPlayer { get; } + public ClientElementCollection ElementCollection { get; } = new(); + + public TestingServer TestingServer { get; } + public TPlayer ServerPlayer { get; } public TestingClient Client { get; } - public TimeContext TimeContext { get; } - public ClientEnvironment(TestingServer testingServer, TestingPlayer testingPlayer) + public ClientPlayer(TestingServer testingServer, TPlayer testingPlayer) : base(testingPlayer.Id) { this.TestingServer = testingServer; this.ServerPlayer = testingPlayer; - this.ClientPlayer = new ClientPlayer(this); this.Client = testingPlayer.Client as TestingClient; - this.TimeContext = new(); this.Client.PacketSent += HandlePacketSent; this.ServerPlayer.Disconnected += HandleDisconnected; - //TestingServer.NetWrapperMock + + this.ElementCollection.Add(this); + this.Client.SendPacket(new RpcPacket + { + FunctionId = Packets.Enums.RpcFunctions.INITIAL_DATA_STREAM + }); } private void HandleDisconnected(Player sender, PlayerQuitEventArgs e) { - if(sender.Client is TestingClient testingClient) + if (sender.Client is TestingClient testingClient) testingClient.PacketSent -= HandlePacketSent; } @@ -89,16 +128,55 @@ private void HandlePacketSent(TestingClient client, Packet packet) { switch (packet) { + case RpcPacket rpcPacket: + this.TestingServer.GetRequiredService().HandlePacket(client, rpcPacket); + break; case SetElementPositionRpcPacket setElementPositionRpcPacket: - if (setElementPositionRpcPacket.ElementId == this.ServerPlayer.Id) { - this.ClientPlayer.SetPosition(setElementPositionRpcPacket.Position, EventSide.Server); + var element = this.ElementCollection.Get(setElementPositionRpcPacket.ElementId); + element.SetPosition(setElementPositionRpcPacket.Position, EventSide.Server); } - else + break; + case AddEntityPacket addEntityPacket: + + break; + case PlayerListPacket playerListPacket: + ; + break; + case SetSyncSettingsPacket setSyncSettingsPacket: + + break; + case SetSyncIntervalPacket setSyncIntervalPacket: + + break; + case PlayerPureSyncPacket playerPureSyncPacket: + + break; + case ReturnSyncPacket returnSyncPacket: + + break; + case ChangeNicknamePacket changeNicknamePacket: { - throw new InvalidOperationException($"Element with ID {setElementPositionRpcPacket.ElementId} does not match Player ID {this.ServerPlayer.Id}. Ensure the correct packet is being sent."); + var element = this.ElementCollection.Get(changeNicknamePacket.PlayerId); + element.SetName(changeNicknamePacket.Name, EventSide.Server); } + break; + default: + throw new NotImplementedException(packet.ToString()); } } + + /// + /// This is reimplementation of this function: void CNetAPI::DoPulse() + /// + public void SynchronizeWithServer() + { + var packet = new PlayerPureSyncPacket + { + Position = this.Position, + }; + + this.Client.SendPacket(packet); + } } diff --git a/SlipeServer.Server.TestTools/TestingServer.cs b/SlipeServer.Server.TestTools/TestingServer.cs index 94c0e1d7..00c649b0 100644 --- a/SlipeServer.Server.TestTools/TestingServer.cs +++ b/SlipeServer.Server.TestTools/TestingServer.cs @@ -20,6 +20,7 @@ using System.Linq; using ILogger = Microsoft.Extensions.Logging.ILogger; using System.Diagnostics.Tracing; +using SlipeServer.Server.PacketHandling.Handlers.Rpc; namespace SlipeServer.Server.TestTools; @@ -116,6 +117,7 @@ private void SetupSendPacketMocks() public static void ConfigureOverrides(IServiceCollection services) { var httpServerMock = new Mock(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(httpServerMock.Object); services.AddLogging(); @@ -207,6 +209,12 @@ public void VerifyLuaEventTriggered(string eventName, TPlayer to, Element source public uint GenerateBinaryAddress() => ++this.binaryAddressCounter; + public ClientPlayer CreateClientPlayer(TPlayer player) + { + var clientPlayer = new ClientPlayer(this, player); + this.FlushPacketQueueHandler(); + return clientPlayer; + } /// /// Starts the networking interfaces, allowing clients to connect and packets to be sent out to clients. diff --git a/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs b/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs deleted file mode 100644 index 3fcbe3c6..00000000 --- a/SlipeServer.Server.Tests/Integration/ClientEnvironmentTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using FluentAssertions; -using FluentAssertions.Execution; -using SlipeServer.Server.Elements; -using SlipeServer.Server.Elements.Events; -using SlipeServer.Server.TestTools; -using System.Numerics; -using System.Threading; -using Xunit; - -namespace SlipeServer.Server.Tests.Integration; - -public class ClientEnvironmentTests -{ - [Fact] - public void SettingGettingPositionShouldWork() - { - var server = new TestingServer(); - server.AddDefaultPacketHandlers(); - var player = server.AddFakePlayer(); - var clientEnvironment = new ClientEnvironment(server, player); - - using var monitor = clientEnvironment.ClientPlayer.Monitor(); - player.Position = new Vector3(3, 3, 3); - var clientPositionWas = player.Position; - clientEnvironment.ClientPlayer.SetPosition(new Vector3(5, 0, 0)); - - clientEnvironment.ClientPlayer.SynchronizeWithServer(); - server.FlushPacketQueueHandler(); - - using var _ = new AssertionScope(); - - clientPositionWas.Should().Be(new Vector3(3, 3, 3)); - player.Position.Should().Be(new Vector3(5, 0, 0)); - } -} diff --git a/SlipeServer.Server.Tests/Integration/ClientPlayerTests.cs b/SlipeServer.Server.Tests/Integration/ClientPlayerTests.cs new file mode 100644 index 00000000..7594d401 --- /dev/null +++ b/SlipeServer.Server.Tests/Integration/ClientPlayerTests.cs @@ -0,0 +1,37 @@ +using FluentAssertions; +using FluentAssertions.Execution; +using SlipeServer.Server.TestTools; +using System.Numerics; +using Xunit; + +namespace SlipeServer.Server.Tests.Integration; + +public class ClientPlayerTests +{ + [Fact] + public void SettingGettingPositionShouldWork() + { + var server = new TestingServer(); + server.AddDefaultPacketHandlers(); + var player1 = server.AddFakePlayer(); + player1.Name = "player1"; + var clientPlayer1 = server.CreateClientPlayer(player1); + var player2 = server.AddFakePlayer(); + player2.Name = "player2"; + var clientPlayer2 = server.CreateClientPlayer(player2); + + using var monitor = clientPlayer1.Monitor(); + player1.Position = new Vector3(3, 3, 3); + var wasClientPosition = player1.Position; + clientPlayer1.SetPosition(new Vector3(5, 0, 0)); + + clientPlayer1.SynchronizeWithServer(); + server.FlushPacketQueueHandler(); + + using var _ = new AssertionScope(); + + wasClientPosition.Should().Be(new Vector3(3, 3, 3)); + player1.Position.Should().Be(new Vector3(5, 0, 0)); + clientPlayer2.ElementCollection.Get(player1.Id).Position.Should().Be(new Vector3(5, 0, 0)); + } +} diff --git a/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs b/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs index adb6a8bf..14f46f75 100644 --- a/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs +++ b/SlipeServer.Server/PacketHandling/Factories/PlayerPacketFactory.cs @@ -22,7 +22,10 @@ public static HudComponentVisiblePacket CreateShowHudComponentPacket(HudComponen public static PlayerListPacket CreatePlayerListPacket(Player[] players, bool showInChat = false) { - var packet = new PlayerListPacket(showInChat); + var packet = new PlayerListPacket + { + ShowInChat = showInChat + }; foreach (var player in players) { @@ -139,7 +142,11 @@ public static PlayerWastedPacket CreateWastedPacket( public static ChangeNicknamePacket CreateNicknameChangePacket(Player player) { - return new ChangeNicknamePacket(player.Id, player.Name); + return new ChangeNicknamePacket + { + PlayerId = player.Id, + Name = player.Name + }; } public static UpdateInfoPacket CreateUpdateInfoPacket(Version version, bool mandatory = true)