diff --git a/src/NosCore.Packets/Deserializer.cs b/src/NosCore.Packets/Deserializer.cs index 9aa0618..ad96e16 100644 --- a/src/NosCore.Packets/Deserializer.cs +++ b/src/NosCore.Packets/Deserializer.cs @@ -322,7 +322,13 @@ public IPacket Deserialize(string packetContent) var separator = packetIndexAttribute.SpecialSeparator; if (packetIndexAttribute is PacketListIndexAttribute ind) { - separator = ind.ListSeparator ?? (typeof(IPacket).IsAssignableFrom(subType) ? separator : "."); + // For IPacket (sub-packet) lists, SpecialSeparator on the list names + // the *inner field* separator inside each sub-packet — not the list + // item separator. The list items are tokenised by the outer packet's + // whitespace. Only ListSeparator (when explicitly set) defines how + // items are joined. For primitive lists, SpecialSeparator remains the + // inter-item separator, defaulting to ".". + separator = ind.ListSeparator ?? (typeof(IPacket).IsAssignableFrom(subType) ? null : separator ?? "."); } if (isMaxIndex && string.IsNullOrWhiteSpace(separator)) { @@ -359,9 +365,17 @@ public IPacket Deserialize(string packetContent) var packSeperators = subType.GetProperties() .Select(prop => prop.GetCustomAttribute()).Where(s => s != null).ToList(); + // Fall back to the outer list's SpecialSeparator when the next property + // doesn't override it. Sub-packets like ClinitSubPacket don't decorate + // their own properties, so without this fallback the IndexOf looks for + // "." in strings like "5130502|99|68|547|Trippybell" (not present) and + // every loop iteration appends the whole subpacket — producing + // "X|..X|..X|..X|..X|.." that then fails to parse. + var fieldSeparator = packetIndexAttribute.SpecialSeparator ?? "."; for (var index = 0; index < packSeperators.Count; index++) { - var c = index == packSeperators.Count - 1 ? -1 : subpacket.IndexOf(packSeperators[index + 1]!.SpecialSeparator ?? ".", StringComparison.Ordinal); + var isLast = index == packSeperators.Count - 1; + var c = isLast ? -1 : subpacket.IndexOf(packSeperators[index + 1]!.SpecialSeparator ?? fieldSeparator, StringComparison.Ordinal); if (c == -1) { toConvert += subpacket; diff --git a/src/NosCore.Packets/NosCore.Packets.csproj b/src/NosCore.Packets/NosCore.Packets.csproj index 623103d..b2b7c87 100644 --- a/src/NosCore.Packets/NosCore.Packets.csproj +++ b/src/NosCore.Packets/NosCore.Packets.csproj @@ -12,7 +12,7 @@ https://github.com/NosCoreIO/NosCore.Packets.git nostale, noscore, chickenapi, nostale private server source, nostale emulator - 17.0.0 + 17.1.0 false NosCore's Packets (Nostale packets) defined over classes MIT diff --git a/test/NosCore.Packets.Tests/DeserializerTest.cs b/test/NosCore.Packets.Tests/DeserializerTest.cs index 7bf5544..bb3e018 100644 --- a/test/NosCore.Packets.Tests/DeserializerTest.cs +++ b/test/NosCore.Packets.Tests/DeserializerTest.cs @@ -417,6 +417,35 @@ public void DeserializeWithGuid() Assert.AreEqual("458E0876581FA9A6EFE00A28AA8E75F2", packet.Md5String); } + [TestMethod] + public void PacketWithListOfSubPacketsWithoutPerPropertySeparatorsSplitsCorrectly() + { + // ClinitPacket is `[PacketListIndex(0, SpecialSeparator = "|")]` over + // ClinitSubPacket whose five properties don't decorate their own + // SpecialSeparator. Prior to 17.1.0 the list-of-sub-packets path + // fell back to "." when looking for each field boundary, which + // wasn't present, so the full sub-packet was appended once per + // property — yielding "X|..X|..X|..X|..X|.." that then failed to parse. + var deserializer = new Deserializer(new[] + { + typeof(ClinitPacket), + typeof(ClinitSubPacket), + }); + + var packet = (ClinitPacket)deserializer.Deserialize( + "clinit 5130502|99|68|547|Trippybell 7170218|99|94|325|Hamu"); + + Assert.IsNotNull(packet.SubPackets); + Assert.HasCount(2, packet.SubPackets!); + Assert.AreEqual(5130502L, packet.SubPackets[0]!.CharacterId); + Assert.AreEqual((byte)99, packet.SubPackets[0]!.Level); + Assert.AreEqual((byte)68, packet.SubPackets[0]!.HeroLevel); + Assert.AreEqual(547, packet.SubPackets[0]!.Compliment); + Assert.AreEqual("Trippybell", packet.SubPackets[0]!.CharacterName); + Assert.AreEqual(7170218L, packet.SubPackets[1]!.CharacterId); + Assert.AreEqual("Hamu", packet.SubPackets[1]!.CharacterName); + } + [TestMethod] public void PacketWithEmptySpecialSeparatorSplitsEachCharIntoField() {