diff --git a/Source/Client/Desyncs/SyncCoordinator.cs b/Source/Client/Desyncs/SyncCoordinator.cs index fa002235..b54efa37 100644 --- a/Source/Client/Desyncs/SyncCoordinator.cs +++ b/Source/Client/Desyncs/SyncCoordinator.cs @@ -4,6 +4,7 @@ using Multiplayer.Client.Desyncs; using Multiplayer.Client.Util; using Multiplayer.Common; +using Multiplayer.Common.Networking.Packet; using RimWorld; using Verse; @@ -125,7 +126,7 @@ private void HandleDesync(ClientSyncOpinion oldOpinion, ClientSyncOpinion newOpi var remote = !oldOpinion.isLocalClientsOpinion ? oldOpinion : newOpinion; var diffAt = FindTraceHashesDiffTick(local, remote, out var found); - Multiplayer.Client.Send(Packets.Client_Desynced, local.startTick, diffAt); + Multiplayer.Client.Send(new ClientDesyncedPacket(local.startTick, diffAt)); Multiplayer.session.desyncTracesFromHost = null; MpUI.ClearWindowStack(); diff --git a/Source/Client/Networking/NetworkingSteam.cs b/Source/Client/Networking/NetworkingSteam.cs index 90192f5c..ab81b86f 100644 --- a/Source/Client/Networking/NetworkingSteam.cs +++ b/Source/Client/Networking/NetworkingSteam.cs @@ -76,7 +76,7 @@ public class SteamServerConn(CSteamID remoteId, ushort clientChannel) : SteamBas { private readonly Stopwatch keepAliveTimer = new(); - public override void Send(Packets id, byte[] message, bool reliable = true) + protected override void Send(Packets id, byte[] message, bool reliable = true) { if (id == Packets.Server_KeepAlive) keepAliveTimer.Restart(); base.Send(id, message, reliable); diff --git a/Source/Client/Networking/State/ClientPlayingState.cs b/Source/Client/Networking/State/ClientPlayingState.cs index 0e14e8f2..88277a76 100644 --- a/Source/Client/Networking/State/ClientPlayingState.cs +++ b/Source/Client/Networking/State/ClientPlayingState.cs @@ -113,25 +113,20 @@ public void HandleCursor(ServerCursorPacket packet) player.dragStart = data.HasDrag ? new Vector3(data.dragX, 0, data.dragZ) : PlayerInfo.Invalid; } - [PacketHandler(Packets.Server_Selected)] - public void HandleSelected(ByteReader data) + [TypedPacketHandler] + public void HandleSelected(ServerSelectedPacket packet) { - int playerId = data.ReadInt32(); - var player = Multiplayer.session.GetPlayerInfo(playerId); + var player = Multiplayer.session.GetPlayerInfo(packet.playerId); if (player == null) return; - bool reset = data.ReadBool(); - - if (reset) - player.selectedThings.Clear(); + var data = packet.data; + if (data.reset) player.selectedThings.Clear(); - int[] add = data.ReadPrefixedInts(); - for (int i = 0; i < add.Length; i++) - player.selectedThings[add[i]] = Time.realtimeSinceStartup; + foreach (var id in data.newlySelectedIds) + player.selectedThings[id] = Time.realtimeSinceStartup; - int[] remove = data.ReadPrefixedInts(); - for (int i = 0; i < remove.Length; i++) - player.selectedThings.Remove(remove[i]); + foreach (var id in data.unselectedIds) + player.selectedThings.Remove(id); } [TypedPacketHandler] @@ -156,15 +151,13 @@ public void HandleMapResponse(ByteReader data) // todo Multiplayer.client.Send(Packets.CLIENT_MAP_LOADED); } - [PacketHandler(Packets.Server_Notification)] - public void HandleNotification(ByteReader data) + [TypedPacketHandler] + public void HandleNotification(ServerNotificationPacket packet) { - string key = data.ReadString(); - string[] args = data.ReadPrefixedStrings(); - - var msg = key.Translate(Array.ConvertAll(args, s => (NamedArgument)s)); + var namedArgs = Array.ConvertAll(packet.args, s => (NamedArgument)s); + var msg = packet.key.Translate(namedArgs); Messages.Message(msg, MessageTypeDefOf.SilentInput, false); - ServerLog.Log($"Notification: {msg} ({key}, {args.Join(", ")})"); + ServerLog.Log($"Notification: {msg} ({packet.key}, {packet.args.Join(", ")})"); } [TypedPacketHandler] @@ -178,34 +171,25 @@ public void HandleFreeze(ServerFreezePacket packet) TickPatch.frozenAt = packet.gameTimer; } - [PacketHandler(Packets.Server_Traces, allowFragmented: true)] - public void HandleTraces(ByteReader data) + [TypedPacketHandler] + public void HandleTraces(ServerTracesPacket packet) { - var type = data.ReadEnum(); - - if (type == TracesPacket.Request) + if (packet.mode == ServerTracesPacket.Mode.Request) { - var tick = data.ReadInt32(); - var diffAt = data.ReadInt32(); - var playerId = data.ReadInt32(); - - var info = Multiplayer.game.sync.knownClientOpinions.FirstOrDefault(b => b.startTick == tick); - var response = info?.GetFormattedStackTracesForRange(diffAt); + var info = Multiplayer.game.sync.knownClientOpinions.FirstOrDefault(b => b.startTick == packet.tick); + var response = info?.GetFormattedStackTracesForRange(packet.diffAt) ?? "Traces not available"; - connection.Send(Packets.Client_Traces, TracesPacket.Response, playerId, GZipStream.CompressString(response)); + connection.Send(new ClientTracesPacket + { playerId = packet.playerId, rawTraces = GZipStream.CompressString(response) }); } - else if (type == TracesPacket.Transfer) + else if (packet.mode == ServerTracesPacket.Mode.Transfer) { - var traces = data.ReadPrefixedBytes(); - Multiplayer.session.desyncTracesFromHost = GZipStream.UncompressString(traces); + Multiplayer.session.desyncTracesFromHost = GZipStream.UncompressString(packet.rawTraces); } } - [PacketHandler(Packets.Server_Debug)] - public void HandleDebug(ByteReader data) - { - Rejoiner.DoRejoin(); - } + [TypedPacketHandler] + public void HandleDebug(ServerDebugPacket _) => Rejoiner.DoRejoin(); [TypedPacketHandler] public void HandleSetFaction(ServerSetFactionPacket packet) diff --git a/Source/Client/Saving/ReplayConnection.cs b/Source/Client/Saving/ReplayConnection.cs index 4dfc691f..bbeca7ef 100644 --- a/Source/Client/Saving/ReplayConnection.cs +++ b/Source/Client/Saving/ReplayConnection.cs @@ -7,7 +7,7 @@ public class ReplayConnection : ConnectionBase { public static Action replayCmdEvent; - public override void Send(Packets id, byte[] message, bool reliable = true) + protected override void Send(Packets id, byte[] message, bool reliable = true) { if (id == Packets.Client_Command) replayCmdEvent?.Invoke(DeserializeCmd(new ByteReader(message))); diff --git a/Source/Client/UI/PlayerCursors.cs b/Source/Client/UI/PlayerCursors.cs index 63bfcd01..d8c8bc99 100644 --- a/Source/Client/UI/PlayerCursors.cs +++ b/Source/Client/UI/PlayerCursors.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Multiplayer.Common; using Multiplayer.Common.Networking.Packet; using RimWorld.Planet; using UnityEngine; @@ -64,8 +63,6 @@ private void SendSelected() { if (Current.ProgramState != ProgramState.Playing) return; - var writer = new ByteWriter(); - int mapId = Find.CurrentMap?.Index ?? -1; if (WorldRendererUtility.WorldSelected) mapId = -1; @@ -80,17 +77,14 @@ private void SendSelected() var selected = new HashSet(Find.Selector.selected.OfType().Select(t => t.thingIDNumber)); - var add = new List(selected.Except(lastSelected)); - var remove = new List(lastSelected.Except(selected)); - - if (!reset && add.Count == 0 && remove.Count == 0) return; + var add = selected.Except(lastSelected).ToArray(); + var remove = lastSelected.Except(selected).ToArray(); - writer.WriteBool(reset); - writer.WritePrefixedInts(add); - writer.WritePrefixedInts(remove); + if (!reset && add.Length == 0 && remove.Length == 0) return; lastSelected = selected; - Multiplayer.Client.Send(Packets.Client_Selected, writer.ToArray()); + Multiplayer.Client.Send(new ClientSelectedPacket + { reset = reset, newlySelectedIds = add, unselectedIds = remove }); } } diff --git a/Source/Common/MultiplayerServer.cs b/Source/Common/MultiplayerServer.cs index 2a89035f..71dab176 100644 --- a/Source/Common/MultiplayerServer.cs +++ b/Source/Common/MultiplayerServer.cs @@ -214,30 +214,18 @@ public void Enqueue(Action action) public void SendToPlaying(T packet, bool reliable = true, ServerPlayer? excluding = null) where T : IPacket { - var materialized = packet.Serialize(); - SendToPlaying(materialized.id, materialized.data, reliable, excluding); - } - - public void SendToPlaying(Packets id, object[] data) => SendToPlaying(id, ByteWriter.GetBytes(data)); - - public void SendToIngame(Packets id, byte[] data, bool reliable = true, ServerPlayer? excluding = null) - { - foreach (ServerPlayer player in PlayingIngamePlayers) + var serialized = packet.Serialize(); + foreach (ServerPlayer player in PlayingPlayers) if (player != excluding) - player.conn.Send(id, data, reliable); + player.conn.Send(serialized, reliable); } public void SendToIngame(T packet, bool reliable = true, ServerPlayer? excluding = null) where T : IPacket { var serialized = packet.Serialize(); - SendToIngame(serialized.id, serialized.data, reliable, excluding); - } - - public void SendToPlaying(Packets id, byte[] data, bool reliable = true, ServerPlayer? excluding = null) - { - foreach (ServerPlayer player in PlayingPlayers) + foreach (ServerPlayer player in PlayingIngamePlayers) if (player != excluding) - player.conn.Send(id, data, reliable); + player.conn.Send(serialized, reliable); } public ServerPlayer? GetPlayer(string username) @@ -256,10 +244,8 @@ public void SendChat(string msg) SendToPlaying(ServerChatPacket.Create(msg)); } - public void SendNotification(string key, params string[] args) - { - SendToPlaying(Packets.Server_Notification, new object[] { key, args }); - } + public void SendNotification(string key, params string[] args) => + SendToPlaying(new ServerNotificationPacket(key) { args = args }); public void RegisterChatCmd(string cmdName, ChatCmdHandler handler) => chatCmdManager.AddCommandHandler(cmdName, handler); diff --git a/Source/Common/Networking/ConnectionBase.cs b/Source/Common/Networking/ConnectionBase.cs index 54151124..a4a130da 100644 --- a/Source/Common/Networking/ConnectionBase.cs +++ b/Source/Common/Networking/ConnectionBase.cs @@ -34,9 +34,7 @@ public void ChangeState(ConnectionStateEnum state) StateObj?.StartState(); } - public void Send(Packets id) => Send(id, Array.Empty()); - - public void Send(Packets id, params object[] msg) => Send(id, ByteWriter.GetBytes(msg)); + public void Send(Packets id) => Send(id, []); public void Send(SerializedPacket packet, bool reliable = true) => Send(packet.id, packet.data, reliable); @@ -56,7 +54,7 @@ public void Send(T packet, bool reliable = true) where T : struct, IPacket SendRaw(writer.ToArray(), reliable); } - public virtual void Send(Packets id, byte[] message, bool reliable = true) + protected virtual void Send(Packets id, byte[] message, bool reliable = true) { if (State == ConnectionStateEnum.Disconnected) return; @@ -146,8 +144,6 @@ public void SendFragmented(Packets id, byte[] message) public void SendFragmented(SerializedPacket packet) => SendFragmented(packet.id, packet.data); - public void SendFragmented(Packets id, params object[] msg) => SendFragmented(id, ByteWriter.GetBytes(msg)); - protected abstract void SendRaw(byte[] raw, bool reliable = true); public virtual void HandleReceiveRaw(ByteReader data, bool reliable) diff --git a/Source/Common/Networking/Packet/DebugPacket.cs b/Source/Common/Networking/Packet/DebugPacket.cs new file mode 100644 index 00000000..2282f1c0 --- /dev/null +++ b/Source/Common/Networking/Packet/DebugPacket.cs @@ -0,0 +1,17 @@ +namespace Multiplayer.Common.Networking.Packet; + +[PacketDefinition(Packets.Server_Debug)] +public record struct ServerDebugPacket : IPacket +{ + public void Bind(PacketBuffer buf) + { + } +} + +[PacketDefinition(Packets.Client_Debug)] +public record struct ClientDebugPacket : IPacket +{ + public void Bind(PacketBuffer buf) + { + } +} diff --git a/Source/Common/Networking/Packet/DesyncedPacket.cs b/Source/Common/Networking/Packet/DesyncedPacket.cs new file mode 100644 index 00000000..cb5df7a9 --- /dev/null +++ b/Source/Common/Networking/Packet/DesyncedPacket.cs @@ -0,0 +1,14 @@ +namespace Multiplayer.Common.Networking.Packet; + +[PacketDefinition(Packets.Client_Desynced)] +public record struct ClientDesyncedPacket(int tick, int diffAt) : IPacket +{ + public int tick = tick; + public int diffAt = diffAt; + + public void Bind(PacketBuffer buf) + { + buf.Bind(ref tick); + buf.Bind(ref diffAt); + } +} diff --git a/Source/Common/Networking/Packet/IPacket.cs b/Source/Common/Networking/Packet/IPacket.cs index 9acf3392..79f94376 100644 --- a/Source/Common/Networking/Packet/IPacket.cs +++ b/Source/Common/Networking/Packet/IPacket.cs @@ -64,6 +64,9 @@ public static Binder UInt() => public static Binder Enum() where T: Enum => (PacketBuffer buf, ref T obj) => buf.BindEnum(ref obj); + + public static Binder String() => + (PacketBuffer buf, ref string obj) => buf.Bind(ref obj); } public static class BinderExtensions diff --git a/Source/Common/Networking/Packet/NotificationPacket.cs b/Source/Common/Networking/Packet/NotificationPacket.cs new file mode 100644 index 00000000..d06fb6fd --- /dev/null +++ b/Source/Common/Networking/Packet/NotificationPacket.cs @@ -0,0 +1,15 @@ +namespace Multiplayer.Common.Networking.Packet; + +[PacketDefinition(Packets.Server_Notification)] +public record struct ServerNotificationPacket(string key) : IPacket +{ + public string key = key; + public string[] args = []; + + public void Bind(PacketBuffer buf) + { + buf.Bind(ref key); + buf.Bind(ref args, BinderOf.String()); + } +} + diff --git a/Source/Common/Networking/Packet/SelectedPacket.cs b/Source/Common/Networking/Packet/SelectedPacket.cs new file mode 100644 index 00000000..b328dab7 --- /dev/null +++ b/Source/Common/Networking/Packet/SelectedPacket.cs @@ -0,0 +1,30 @@ +namespace Multiplayer.Common.Networking.Packet; + + +[PacketDefinition(Packets.Server_Selected)] +public record struct ServerSelectedPacket(int playerId, ClientSelectedPacket data) : IPacket +{ + public int playerId = playerId; + public ClientSelectedPacket data = data; + + public void Bind(PacketBuffer buf) + { + buf.Bind(ref playerId); + buf.Bind(ref data); + } +} + +[PacketDefinition(Packets.Client_Selected)] +public record struct ClientSelectedPacket : IPacket +{ + public bool reset; + public int[] newlySelectedIds; + public int[] unselectedIds; + + public void Bind(PacketBuffer buf) + { + buf.Bind(ref reset); + buf.Bind(ref newlySelectedIds, BinderOf.Int(), maxLength: 200); + buf.Bind(ref unselectedIds, BinderOf.Int(), maxLength: 200); + } +} diff --git a/Source/Common/Networking/Packet/TracesPacket.cs b/Source/Common/Networking/Packet/TracesPacket.cs new file mode 100644 index 00000000..b655cf34 --- /dev/null +++ b/Source/Common/Networking/Packet/TracesPacket.cs @@ -0,0 +1,58 @@ +namespace Multiplayer.Common.Networking.Packet; + +[PacketDefinition(Packets.Client_Traces, allowFragmented: true)] +public record struct ClientTracesPacket : IPacket +{ + public int playerId; + public byte[] rawTraces; + + public void Bind(PacketBuffer buf) + { + buf.Bind(ref playerId); + buf.BindRemaining(ref rawTraces); + } +} + +[PacketDefinition(Packets.Server_Traces, allowFragmented: true)] +public record struct ServerTracesPacket : IPacket +{ + public enum Mode : byte + { + Request, Transfer + } + + public Mode mode; + + public int tick; + public int diffAt; + public int playerId; + + // Used in transfer only + public byte[] rawTraces; + + public static ServerTracesPacket Request(int tick, int diffAt, int playerId) => new() + { + mode = Mode.Request, + tick = tick, + diffAt = diffAt, + playerId = playerId + }; + + public static ServerTracesPacket Transfer(byte[] rawTraces) => + new() { mode = Mode.Transfer, rawTraces = rawTraces }; + + public void Bind(PacketBuffer buf) + { + buf.BindEnum(ref mode); + if (mode == Mode.Request) + { + buf.Bind(ref tick); + buf.Bind(ref diffAt); + buf.Bind(ref playerId); + } + else + { + buf.BindRemaining(ref rawTraces); + } + } +} diff --git a/Source/Common/Networking/State/ServerJoiningState.cs b/Source/Common/Networking/State/ServerJoiningState.cs index aa448594..63ad131f 100644 --- a/Source/Common/Networking/State/ServerJoiningState.cs +++ b/Source/Common/Networking/State/ServerJoiningState.cs @@ -43,7 +43,7 @@ private void HandleProtocol(ClientProtocolPacket packet) if (packet.protocolVersion != MpVersion.Protocol) Player.Disconnect(MpDisconnectReason.Protocol, ByteWriter.GetBytes(MpVersion.Version, MpVersion.Protocol)); else - Player.conn.Send(new ServerProtocolOkPacket(Server.settings.hasPassword)); + Player.SendPacket(new ServerProtocolOkPacket(Server.settings.hasPassword)); } private void HandleUsername(ClientUsernamePacket packet) @@ -87,7 +87,7 @@ private async Task RequestInitData() var completionSource = Server.StartInitData(); try { - Player.conn.Send(new ServerInitDataRequestPacket(Server.settings.syncConfigs)); + Player.SendPacket(new ServerInitDataRequestPacket(Server.settings.syncConfigs)); ServerLog.Verbose("Sent initial data request"); initData = await TypedPacketOrNull(); diff --git a/Source/Common/Networking/State/ServerPlayingState.cs b/Source/Common/Networking/State/ServerPlayingState.cs index 92f5798c..d2dfcf36 100644 --- a/Source/Common/Networking/State/ServerPlayingState.cs +++ b/Source/Common/Networking/State/ServerPlayingState.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using Multiplayer.Common.Networking.Packet; @@ -20,26 +19,15 @@ public void HandleRejoin(ByteReader data) Player.ResetTimeVotes(); } - [PacketHandler(Packets.Client_Desynced)] - public void HandleDesynced(ByteReader data) - { - var tick = data.ReadInt32(); - var diffAt = data.ReadInt32(); - - Server.playerManager.OnDesync(Player, tick, diffAt); - } + [TypedPacketHandler] + public void HandleDesynced(ClientDesyncedPacket packet) => + Server.playerManager.OnDesync(Player, packet.tick, packet.diffAt); - [PacketHandler(Packets.Client_Traces, allowFragmented: true)] - public void HandleTraces(ByteReader data) + [TypedPacketHandler] + public void HandleTraces(ClientTracesPacket packet) { - var type = data.ReadEnum(); - - if (type == TracesPacket.Response && Player.IsHost) - { - var playerId = data.ReadInt32(); - var traces = data.ReadPrefixedBytes(); - Server.GetPlayer(playerId)?.SendPacket(Packets.Server_Traces, new object[] { TracesPacket.Transfer, traces }); - } + if (!Player.IsHost) return; + Server.GetPlayer(packet.playerId)?.SendPacket(ServerTracesPacket.Transfer(packet.rawTraces)); } [TypedPacketHandler] @@ -117,20 +105,9 @@ public void HandleCursor(ClientCursorPacket clientPacket) Server.SendToIngame(serverPacket, reliable: false, excluding: Player); } - [PacketHandler(Packets.Client_Selected)] - public void HandleSelected(ByteReader data) - { - bool reset = data.ReadBool(); - - var writer = new ByteWriter(); - - writer.WriteInt32(Player.id); - writer.WriteBool(reset); - writer.WritePrefixedInts(data.ReadPrefixedInts(200)); - writer.WritePrefixedInts(data.ReadPrefixedInts(200)); - - Server.SendToPlaying(Packets.Server_Selected, writer.ToArray(), excluding: Player); - } + [TypedPacketHandler] + public void HandleSelected(ClientSelectedPacket packet) => + Server.SendToPlaying(new ServerSelectedPacket(Player.id, packet)); [TypedPacketHandler] public void HandlePing(ClientPingLocPacket packet) => @@ -184,15 +161,15 @@ public void HandleAutosaving(ByteReader data) Server.worldData.TryStartJoinPointCreation(); } - [PacketHandler(Packets.Client_Debug)] - public void HandleDebug(ByteReader data) + [TypedPacketHandler] + public void HandleDebug(ClientDebugPacket _) { // todo restrict handling Server.worldData.mapCmds.Clear(); Server.gameTimer = Server.startingTimer; - Server.SendToPlaying(Packets.Server_Debug, Array.Empty()); + Server.SendToPlaying(new ServerDebugPacket()); } [TypedPacketHandler] @@ -214,9 +191,4 @@ public void HandleSetFaction(ClientSetFactionPacket packet) [TypedPacketHandler] public void HandleFrameTime(ClientFrameTimePacket packet) => Player.frameTime = packet.frameTime; } - - public enum TracesPacket : byte - { - Request, Response, Transfer - } } diff --git a/Source/Common/PlayerManager.cs b/Source/Common/PlayerManager.cs index 75e19780..1f86f340 100644 --- a/Source/Common/PlayerManager.cs +++ b/Source/Common/PlayerManager.cs @@ -103,7 +103,7 @@ public void SetDisconnected(ConnectionBase conn, MpDisconnectReason reason) public void OnDesync(ServerPlayer player, int tick, int diffAt) { player.UpdateStatus(PlayerStatus.Desynced); - server.HostPlayer.SendPacket(Packets.Server_Traces, new object[] { TracesPacket.Request, tick, diffAt, player.id }); + server.HostPlayer.SendPacket(ServerTracesPacket.Request(tick, diffAt, player.id)); player.ResetTimeVotes(); diff --git a/Source/Common/ServerPlayer.cs b/Source/Common/ServerPlayer.cs index 6584661b..52881be1 100644 --- a/Source/Common/ServerPlayer.cs +++ b/Source/Common/ServerPlayer.cs @@ -73,21 +73,14 @@ public void Disconnect(MpDisconnectReason reason, byte[]? data = null) Server.playerManager.SetDisconnected(conn, reason); } - public void SendKeepAlivePacket() => - conn.Send(new ServerKeepAlivePacket(keepAliveId), false); - - public void SendPacket(Packets packet, byte[] data, bool reliable = true) - { - conn.Send(packet, data, reliable); - } + public void SendPacket(T packet, bool reliable = true) where T : struct, IPacket => + conn.Send(packet, reliable); - public void SendPacket(Packets packet, object[] data) - { - conn.Send(packet, data); - } + public void SendKeepAlivePacket() => + SendPacket(new ServerKeepAlivePacket(keepAliveId), false); public void SendPlayerList() => - conn.Send(ServerPlayerListPacket.List(Server.JoinedPlayers.Select(p => p.PlayerInfoPacket()))); + SendPacket(ServerPlayerListPacket.List(Server.JoinedPlayers.Select(p => p.PlayerInfoPacket()))); public ServerPlayerListPacket.PlayerInfo PlayerInfoPacket() => new() { @@ -134,7 +127,7 @@ public void ResetTimeVotes() ); } - public void SendMsg(string msg) => conn.Send(ServerChatPacket.Create(msg)); + public void SendMsg(string msg) => SendPacket(ServerChatPacket.Create(msg)); } public enum PlayerStatus : byte diff --git a/Source/Common/Version.cs b/Source/Common/Version.cs index e79e5562..8fbc7fcc 100644 --- a/Source/Common/Version.cs +++ b/Source/Common/Version.cs @@ -6,7 +6,7 @@ namespace Multiplayer.Common public static class MpVersion { public const string SimpleVersion = "0.11.0"; - public const int Protocol = 51; + public const int Protocol = 52; public static readonly string? GitHash = Assembly.GetExecutingAssembly() .GetCustomAttributes() diff --git a/Source/Tests/PacketTest.cs b/Source/Tests/PacketTest.cs index a9144633..50a0ac35 100644 --- a/Source/Tests/PacketTest.cs +++ b/Source/Tests/PacketTest.cs @@ -1,4 +1,5 @@ using System.Text; +using FluentAssertions; using Multiplayer.Common; using Multiplayer.Common.Networking.Packet; @@ -221,6 +222,26 @@ private static IEnumerable RoundtripPackets() ], rawData = [1, 2, 3, 4, 5] }; + + // real code is using GZip compressed content for the traces, but we are only testing on the wire representation + // and are treating the bytes as opaque, so it doesn't matter + yield return new ClientTracesPacket { playerId = 123, rawTraces = "trace 1\ntrace 2"u8.ToArray()}; + yield return ServerTracesPacket.Request(9001, 1000, 5); + yield return ServerTracesPacket.Transfer("trace 1\ntrace 2"u8.ToArray()); + + yield return new ServerNotificationPacket("key"); + yield return new ServerNotificationPacket("key") { args = ["1", "2", "3"] }; + + yield return new ClientSelectedPacket + { newlySelectedIds = [1, 2, 3], unselectedIds = [4, 5, 6], reset = false }; + yield return new ClientSelectedPacket { newlySelectedIds = [999], unselectedIds = [111], reset = true }; + + yield return new ServerSelectedPacket(1, + new ClientSelectedPacket { newlySelectedIds = [1, 10, 100], reset = false, unselectedIds = [] }); + + yield return new ClientDesyncedPacket(1234, 4567); + yield return new ClientDesyncedPacket(0, 0); + yield return new ClientDesyncedPacket(100, 0); } [TestCaseSource(nameof(RoundtripPackets))] @@ -230,26 +251,7 @@ public void TestRoundtrip(IPacket original) var serialize = binder.Serialize(original); var deserialized = binder.Deserialize(serialize); - var valueFields = original.GetType().GetFields().Where(f => f.FieldType.IsValueType).ToList(); - var rawFields = original.GetType().GetFields().Where(f => !f.FieldType.IsValueType).ToList(); - if (rawFields.Count > 0) - { - // Default record's equality compares reference types by reference meaning e.g., a byte[] of [1,2,3] isn't - // equal to another byte[] of [1, 2, 3] unless it's the same instance (the same reference). In case - // there are such fields, fallback to comparing the raw serialized bytes, and each field individually. - var serializedAgain = binder.Serialize(deserialized); - Assert.That(serialize, Is.EqualTo(serializedAgain)); - - foreach (var valueField in valueFields) - Assert.That(valueField.GetValue(deserialized), Is.EqualTo(valueField.GetValue(original))); - // Unlike byte[].Equals, Is.EqualTo compares by value, not by reference. - foreach (var rawField in rawFields) - Assert.That(rawField.GetValue(deserialized), Is.EqualTo(rawField.GetValue(original))); - } - else - { - Assert.That(deserialized, Is.EqualTo(original)); - } + deserialized.Should().BeEquivalentTo(original, opts => opts.PreferringRuntimeMemberTypes()); } [Test] diff --git a/Source/Tests/Tests.csproj b/Source/Tests/Tests.csproj index cc79c666..202f2406 100644 --- a/Source/Tests/Tests.csproj +++ b/Source/Tests/Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/Source/Tests/packet-serializations/ClientDesyncedPacket.verified.txt b/Source/Tests/packet-serializations/ClientDesyncedPacket.verified.txt new file mode 100644 index 00000000..70843b19 --- /dev/null +++ b/Source/Tests/packet-serializations/ClientDesyncedPacket.verified.txt @@ -0,0 +1,3 @@ +D2-04-00-00-D7-11-00-00 +00-00-00-00-00-00-00-00 +64-00-00-00-00-00-00-00 diff --git a/Source/Tests/packet-serializations/ClientSelectedPacket.verified.txt b/Source/Tests/packet-serializations/ClientSelectedPacket.verified.txt new file mode 100644 index 00000000..849883ba --- /dev/null +++ b/Source/Tests/packet-serializations/ClientSelectedPacket.verified.txt @@ -0,0 +1,2 @@ +00-03-00-00-00-01-00-00-00-02-00-00-00-03-00-00-00-03-00-00-00-04-00-00-00-05-00-00-00-06-00-00-00 (33 bytes) +01-01-00-00-00-E7-03-00-00-01-00-00-00-6F-00-00-00 diff --git a/Source/Tests/packet-serializations/ClientTracesPacket.verified.txt b/Source/Tests/packet-serializations/ClientTracesPacket.verified.txt new file mode 100644 index 00000000..76f2f51a --- /dev/null +++ b/Source/Tests/packet-serializations/ClientTracesPacket.verified.txt @@ -0,0 +1 @@ +7B-00-00-00-74-72-61-63-65-20-31-0A-74-72-61-63-65-20-32 diff --git a/Source/Tests/packet-serializations/ServerNotificationPacket.verified.txt b/Source/Tests/packet-serializations/ServerNotificationPacket.verified.txt new file mode 100644 index 00000000..1ee7fc9b --- /dev/null +++ b/Source/Tests/packet-serializations/ServerNotificationPacket.verified.txt @@ -0,0 +1,2 @@ +03-00-00-00-6B-65-79-00-00-00-00 +03-00-00-00-6B-65-79-03-00-00-00-01-00-00-00-31-01-00-00-00-32-01-00-00-00-33 diff --git a/Source/Tests/packet-serializations/ServerSelectedPacket.verified.txt b/Source/Tests/packet-serializations/ServerSelectedPacket.verified.txt new file mode 100644 index 00000000..816d7099 --- /dev/null +++ b/Source/Tests/packet-serializations/ServerSelectedPacket.verified.txt @@ -0,0 +1 @@ +01-00-00-00-00-03-00-00-00-01-00-00-00-0A-00-00-00-64-00-00-00-00-00-00-00 diff --git a/Source/Tests/packet-serializations/ServerTracesPacket.verified.txt b/Source/Tests/packet-serializations/ServerTracesPacket.verified.txt new file mode 100644 index 00000000..898741ed --- /dev/null +++ b/Source/Tests/packet-serializations/ServerTracesPacket.verified.txt @@ -0,0 +1,2 @@ +00-29-23-00-00-E8-03-00-00-05-00-00-00 +01-74-72-61-63-65-20-31-0A-74-72-61-63-65-20-32