From f927da1976ccd5a96eb5344f31e8e16c7ececd1b Mon Sep 17 00:00:00 2001 From: kis1yi <35934681+kis1yi@users.noreply.github.com> Date: Sun, 20 Sep 2026 18:41:46 +0200 Subject: [PATCH] Fix spawn packet parsing across client localizations --- Library/RSBot.Core/Client/ReferenceManager.cs | 4 +- .../Entity/EntityGroupSpawnBeginResponse.cs | 2 +- .../Entity/EntityGroupSpawnEndResponse.cs | 2 +- .../Agent/Entity/EntitySingleSpawnResponse.cs | 3 +- Library/RSBot.Core/Objects/NpcTalk.cs | 106 ++++++++++++++---- .../Objects/Spawn/SpawnPacketInfo.cs | 2 +- 6 files changed, 92 insertions(+), 27 deletions(-) diff --git a/Library/RSBot.Core/Client/ReferenceManager.cs b/Library/RSBot.Core/Client/ReferenceManager.cs index 0b6f5f0a..7fe3ca38 100644 --- a/Library/RSBot.Core/Client/ReferenceManager.cs +++ b/Library/RSBot.Core/Client/ReferenceManager.cs @@ -23,11 +23,11 @@ public class ReferenceManager public Dictionary TextData { get; } = new(70000); public Dictionary CharacterData { get; } = new(20000); public Dictionary ItemData { get; } = new(30000); - public Dictionary LevelData { get; } = new(128); + public Dictionary LevelData { get; } = new(150); public Dictionary QuestData { get; } = new(2048); public Dictionary SkillData { get; } = new(40000); public Dictionary SkillMasteryData { get; } = new(32); - public Dictionary AbilityItemByOptLevel { get; } = new(512); + public Dictionary AbilityItemByOptLevel { get; } = new(1024); public List SkillByItemOptLevels { get; } = new(1024); public List ExtraAbilityByEquipItemOptLevel { get; } = new(50000); public Dictionary Shops { get; } = new(128); diff --git a/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnBeginResponse.cs b/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnBeginResponse.cs index b8ccfb0b..6ed77281 100644 --- a/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnBeginResponse.cs +++ b/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnBeginResponse.cs @@ -30,7 +30,7 @@ public void Invoke(Packet packet) { Packet = new Packet(0x3019), Type = packet.ReadByte(), - Amount = packet.ReadUShort(), + Amount = Game.ClientType >= GameClientType.Chinese ? packet.ReadULong() : packet.ReadUShort(), }; } } diff --git a/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnEndResponse.cs b/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnEndResponse.cs index 3b62dec0..f454282c 100644 --- a/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnEndResponse.cs +++ b/Library/RSBot.Core/Network/Handler/Agent/Entity/EntityGroupSpawnEndResponse.cs @@ -31,7 +31,7 @@ public void Invoke(Packet packet) packet = Game.SpawnInfo.Packet; packet.Lock(); - for (var i = 0; i < Game.SpawnInfo.Amount; i++) + for (var i = 0UL; i < Game.SpawnInfo.Amount; i++) try { switch (Game.SpawnInfo.Type) diff --git a/Library/RSBot.Core/Network/Handler/Agent/Entity/EntitySingleSpawnResponse.cs b/Library/RSBot.Core/Network/Handler/Agent/Entity/EntitySingleSpawnResponse.cs index 7ffc5141..a2d3efd2 100644 --- a/Library/RSBot.Core/Network/Handler/Agent/Entity/EntitySingleSpawnResponse.cs +++ b/Library/RSBot.Core/Network/Handler/Agent/Entity/EntitySingleSpawnResponse.cs @@ -26,6 +26,7 @@ internal class EntitySingleSpawnResponse : IPacketHandler /// The packet. public void Invoke(Packet packet) { - SpawnManager.Parse(packet); + // 0x3015 contains exactly one entity and includes the single-spawn trailer. + SpawnManager.Parse(packet, isGroup: false); } } diff --git a/Library/RSBot.Core/Objects/NpcTalk.cs b/Library/RSBot.Core/Objects/NpcTalk.cs index 1147c939..b3b753ef 100644 --- a/Library/RSBot.Core/Objects/NpcTalk.cs +++ b/Library/RSBot.Core/Objects/NpcTalk.cs @@ -20,6 +20,26 @@ public class NpcTalk /// public byte[] Options { get; set; } + /// + /// Gets or sets the custom talk name. + /// + public string CustomName { get; set; } + + /// + /// Gets or sets the talk option flags used by supported client formats. + /// + public ulong OptionFlags { get; set; } + + /// + /// Gets or sets the custom talk data type. + /// + public byte CustomType { get; set; } + + /// + /// Gets or sets the custom talk data identifier. + /// + public uint CustomId { get; set; } + /// /// Deserialize from the packet /// @@ -28,29 +48,73 @@ public void Deserialize(Packet packet) { Flag = packet.ReadByte(); - if ((Flag & 2) > 0) + if (Game.ClientType <= GameClientType.Thailand) + { + if ((Flag & 1) != 0) + CustomName = packet.ReadString(); + + if ((Flag & 2) != 0) + Options = packet.ReadBytes(4); + + return; + } + + if (Game.ClientType >= GameClientType.Vietnam && Game.ClientType <= GameClientType.Chinese) { - var count = 4; - if (Game.ClientType > GameClientType.Thailand) - count = packet.ReadByte(); - - if ( - Game.ClientType == GameClientType.Global - || Game.ClientType == GameClientType.Turkey - || Game.ClientType == GameClientType.VTC_Game - || Game.ClientType == GameClientType.RuSro - || Game.ClientType == GameClientType.Korean - || Game.ClientType == GameClientType.Japanese - || Game.ClientType == GameClientType.Taiwan - ) - count = 7; - - Options = packet.ReadBytes(count); + if ((Flag & 1) != 0) + CustomName = packet.ReadString(); + + if ((Flag & 2) != 0) + { + var count = packet.ReadByte(); + Options = packet.ReadBytes(count); + } + + if ((Flag & 4) != 0) + { + CustomType = packet.ReadByte(); + CustomId = packet.ReadUInt(); + } + + return; } - // pandora box, after spawned mobs - if (Flag == 6) - if (packet.ReadByte() == 1) // maybe - packet.ReadUInt(); + if ( + Game.ClientType == GameClientType.Turkey + || Game.ClientType == GameClientType.VTC_Game + || Game.ClientType == GameClientType.Taiwan + || Game.ClientType == GameClientType.Japanese + || Game.ClientType == GameClientType.RuSro + || Game.ClientType == GameClientType.Rigid + ) + { + if ((Flag & 1) != 0) + CustomName = packet.ReadString(); + + if ((Flag & 2) != 0) + OptionFlags = packet.ReadULong(); + + if ((Flag & 4) != 0) + { + CustomType = packet.ReadByte(); + CustomId = packet.ReadUInt(); + } + + return; + } + + if (Game.ClientType == GameClientType.Global || Game.ClientType == GameClientType.Korean) + { + if ((Flag & 1) != 0) + OptionFlags = packet.ReadULong(); + + if ((Flag & 2) != 0) + { + CustomType = packet.ReadByte(); + CustomId = packet.ReadUInt(); + } + + return; + } } } diff --git a/Library/RSBot.Core/Objects/Spawn/SpawnPacketInfo.cs b/Library/RSBot.Core/Objects/Spawn/SpawnPacketInfo.cs index 3315efc7..7786efbb 100644 --- a/Library/RSBot.Core/Objects/Spawn/SpawnPacketInfo.cs +++ b/Library/RSBot.Core/Objects/Spawn/SpawnPacketInfo.cs @@ -18,7 +18,7 @@ internal class SpawnPacketInfo /// /// The amount. /// - public ushort Amount { get; set; } + public ulong Amount { get; set; } /// /// Gets or sets the packet.