feat(property): resend the equipment on ResyncEquipment

The property editor sends ResyncEquipment (1238, no payload)
after the player picks up or sets down a model they carry: 59 live
packets, every one right after PlaceModelResponse. Live answered with no
game message, only replica serializations (packet 0x1b), i.e. the
player's equipment sent again. DLU dropped it.

The handler marks the inventory's equipped items dirty and serializes the
player, so everyone gets the equipment again. That live re-sent the
inventory part of the replica is inferred: the captures show a
serialization but it was not decoded.

Check in game: on your property, in build mode, pick up a model from the
world and place it again a few times, then leave build mode: your hat,
shirt, weapon etc. are all shown on you (and to a second player
watching), none invisible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 03:17:14 -05:00
parent dd116b9794
commit 51981b7bee
5 changed files with 43 additions and 0 deletions

View File

@@ -10,6 +10,8 @@
#include "SkillComponent.h"
#include "SkillMessages.h"
#include "Entity.h"
#include "InventoryComponent.h"
#include "InventoryMessages.h"
#include "PlayerMessages.h"
#include <memory>
@@ -121,3 +123,24 @@ TEST_F(ClientMessagesTests, CasterDeadEndsTheDeadCastersSkill) {
EXPECT_FALSE(skills->HasSkill(1));
EXPECT_TRUE(skills->HasSkill(2));
}
// ResyncEquipment (1238): no payload (59 live packets). The next serialization carries the equipped items again.
TEST_F(ClientMessagesTests, ResyncEquipmentResendsTheEquipment) {
auto msg = FromLiveClientCapture<GameMessages::ResyncEquipment>(std::string(PLAYER_HEADER) + "d604");
CDClientDatabase::Connect(":memory:"); // the inventory looks its component up
CDClientDatabase::ExecuteDML("CREATE TABLE ComponentsRegistry (id INTEGER, component_type INTEGER, component_id INTEGER);");
auto* const inventory = player->AddComponent<InventoryComponent>(-1);
RakNet::BitStream first;
inventory->Serialize(first, false); // clears the dirty flag it starts with
RakNet::BitStream clean;
inventory->Serialize(clean, false);
bool equipmentSent = true;
ASSERT_TRUE(clean.Read(equipmentSent));
EXPECT_FALSE(equipmentSent);
msg.Handle(*player, UNASSIGNED_SYSTEM_ADDRESS);
RakNet::BitStream resent;
inventory->Serialize(resent, false);
ASSERT_TRUE(resent.Read(equipmentSent));
EXPECT_TRUE(equipmentSent);
}