fix: a LUP exhibit's construction always carries the exhibit

LWOLUPExhibitComponent::Deserialize (0x00c63790) reads a flag and the
exhibit's LOT, and that is the only way the client learns which exhibit
is shown. DLU wrote the block on a construction only while it was dirty,
so a player who came into range after the first update saw no exhibit
until the next one was picked, up to 20 seconds later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-11-10 13:26:55 -06:00
parent 1382606bd1
commit d3f5c20323
2 changed files with 27 additions and 2 deletions

View File

@@ -16,8 +16,11 @@ void LUPExhibitComponent::NextLUPExhibit() {
}
void LUPExhibitComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
outBitStream.Write(m_DirtyLUPExhibit);
if (m_DirtyLUPExhibit) {
// LWOLUPExhibitComponent::Deserialize (0x00c63790) only learns the exhibit from this block, so a client the object
// comes into range of gets it too
const bool writeExhibit = bIsInitialUpdate || m_DirtyLUPExhibit;
outBitStream.Write(writeExhibit);
if (writeExhibit) {
outBitStream.Write(m_LUPExhibits[m_LUPExhibitIndex % m_LUPExhibits.size()]);
if (!bIsInitialUpdate) m_DirtyLUPExhibit = false;
}

View File

@@ -19,6 +19,7 @@
#include "InventoryComponent.h"
#include "ItemComponent.h"
#include "LevelProgressionComponent.h"
#include "LUPExhibitComponent.h"
#include "PlayerForcedMovementComponent.h"
#include "Item.h"
#include "ModelComponent.h"
@@ -370,6 +371,27 @@ TEST_F(ReplicaConstructionTest, AConstructionKeepsPendingLevelAndForcedMovementC
EXPECT_EQ(again.GetNumberOfBitsUsed(), 2u);
}
// The client learns the exhibit's LOT only from the component's block (LWOLUPExhibitComponent::Deserialize: a flag
// and the LOT). DLU wrote it on a construction only while it was dirty, so a player who came into range after the
// first update saw no exhibit until the next one was picked.
TEST_F(ReplicaConstructionTest, LupExhibitConstructionAlwaysCarriesTheExhibit) {
Entity exhibit(1009, info);
auto* const component = exhibit.AddComponent<LUPExhibitComponent>(-1);
RakNet::BitStream update;
component->Serialize(update, false);
RakNet::BitStream idle;
component->Serialize(idle, false);
EXPECT_EQ(idle.GetNumberOfBitsUsed(), 1u);
RakNet::BitStream construction;
component->Serialize(construction, true);
RakNet::BitStream expected;
expected.Write1();
expected.Write<LOT>(11121);
ExpectSameBits(construction, expected);
}
// A guild change (docs/Guilds.md) is written once: the client reads guild id, a u8 length and 16-bit characters, and
// redraws the name billboard every time it reads a name.
TEST_F(ReplicaConstructionTest, CharacterGuildChangeIsWrittenOnce) {