From 599f33d4365fc875ab332ef0794cef9650ae9e09 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 10 Nov 2026 13:19:28 -0600 Subject: [PATCH] fix: a phantom physics construction keeps the pending effect change LWOPhantomPhysicsComponent::Deserialize (0x00c34db0) reads the effect behind a flag bit. DLU cleared the dirty flag when it wrote a construction, which goes only to the client the volume came into range of, so the clients already watching it never got an effect change that was waiting for the next update. Co-Authored-By: Claude Opus 5.5 --- .../Physics/PhantomPhysicsComponent.cpp | 3 ++- .../ReplicaConstructionTests.cpp | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/dGame/dComponents/Physics/PhantomPhysicsComponent.cpp b/dGame/dComponents/Physics/PhantomPhysicsComponent.cpp index c5b25ffc1..c29dfed77 100644 --- a/dGame/dComponents/Physics/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/Physics/PhantomPhysicsComponent.cpp @@ -153,7 +153,8 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bI } } - m_EffectInfoDirty = false; + // A construction goes to the one client the volume came into range of; the others still need the change + if (!bIsInitialUpdate) m_EffectInfoDirty = false; } } diff --git a/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp b/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp index 7b0f3d016..a3f6913ee 100644 --- a/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp +++ b/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp @@ -316,6 +316,28 @@ TEST_F(ReplicaConstructionTest, PhantomPhysicsVolumeEffectsAsLive) { EXPECT_FALSE(effect.isDirectional); } +// A construction (the volume coming into one player's range) doesn't use up an effect change the players already +// watching it still have to get. +TEST_F(ReplicaConstructionTest, PhantomPhysicsConstructionKeepsThePendingEffect) { + ConnectRegistry(); + Entity volume(1007, info); + auto* const phantom = volume.AddComponent(-1); + RakNet::BitStream first; + phantom->Serialize(first, false); + const auto idle = first.GetNumberOfBitsUsed(); + + phantom->SetVolumeEffect({ "Friction", "0" }); + RakNet::BitStream construction; + phantom->Serialize(construction, true); + RakNet::BitStream update; + phantom->Serialize(update, false); + EXPECT_EQ(update.GetNumberOfBitsUsed(), 2u + 1 + 32 + 32 + 1 + 1); + RakNet::BitStream again; + phantom->Serialize(again, false); + EXPECT_EQ(again.GetNumberOfBitsUsed(), 2u); + EXPECT_LE(again.GetNumberOfBitsUsed(), idle); +} + // 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) {