fix: write a collectible's id on construction only

LWOCollectibleComponent::Deserialize (0x00c45ec0) reads the 16-bit id
on construction and nothing on an update. DLU wrote the id on every
serialization, so the client read 16 stray bits as the flag bits of the
components after it. The replica decoder follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-11-10 13:20:36 -06:00
parent 599f33d436
commit 8dd279cb45
3 changed files with 23 additions and 2 deletions

View File

@@ -42,8 +42,9 @@ bool CollectibleComponent::CountsFor(const Entity& player) const {
}
}
// LWOCollectibleComponent::Deserialize (0x00c45ec0) reads the id on construction only
void CollectibleComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
outBitStream.Write(GetCollectibleId());
if (isConstruction) outBitStream.Write(GetCollectibleId());
}
bool CollectibleComponent::MsgGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportMsg) {

View File

@@ -389,7 +389,8 @@ namespace {
}
if (r.Bit()) j["onThreatList"] = r.Bit();
} },
{ COLLECTIBLE, [](Reader& r, json& j, bool, const auto&) { j["collectibleID"] = r.Get<int16_t>(); } },
// LWOCollectibleComponent::Deserialize: the id on construction only
{ COLLECTIBLE, [](Reader& r, json& j, bool initial, const auto&) { if (initial) j["collectibleID"] = r.Get<int16_t>(); } },
// LWOPetComponent::Deserialize: everything under the dirty bit, the names on updates too
{ PET, [](Reader& r, json& j, bool, const auto&) {
if (!r.Bit()) return;

View File

@@ -2,6 +2,7 @@
// the player has that mission.
#include "GameDependencies.h"
#include "BitStream.h"
#include "GameMessages.h"
#include "CDClientDatabase.h"
#include "CDClientManager.h"
@@ -60,6 +61,24 @@ TEST_F(CollectibleComponentTests, RequirementMissionIsRead) {
EXPECT_EQ(other.AddComponent<CollectibleComponent>(999, 5)->GetRequirementMission(), -1); // no row
}
// LWOCollectibleComponent::Deserialize reads the 16-bit id on construction and nothing on an update. DLU wrote the id
// on updates too, which the client read as the next components' flag bits.
TEST_F(CollectibleComponentTests, TheIdIsWrittenOnConstructionOnly) {
Entity object(22, info);
auto* const collectible = object.AddComponent<CollectibleComponent>(78, 5);
RakNet::BitStream construction;
collectible->Serialize(construction, true);
ASSERT_EQ(construction.GetNumberOfBitsUsed(), 16u);
int16_t id{};
construction.Read(id);
EXPECT_EQ(id, 5);
RakNet::BitStream update;
collectible->Serialize(update, false);
EXPECT_EQ(update.GetNumberOfBitsUsed(), 0u);
}
TEST_F(CollectibleComponentTests, AchievementsAndMissingMissionsAlwaysCount) {
Entity player(42, info);
player.AddComponent<MissionComponent>(-1);