diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index eabd86caa..f6998d806 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -1074,8 +1074,8 @@ namespace { using enum eReplicaComponentType; return std::array{ POSSESSABLE, MODULE_ASSEMBLY, CONTROLLABLE_PHYSICS, SIMPLE_PHYSICS, RIGID_BODY_PHANTOM_PHYSICS, HAVOK_VEHICLE_PHYSICS, - PHANTOM_PHYSICS, SOUND_TRIGGER, RACING_SOUND_TRIGGER, BUFF, DESTROYABLE, COLLECTIBLE, PET, CHARACTER, ITEM, INVENTORY, - SCRIPT, SKILL, BASE_COMBAT_AI, QUICK_BUILD, MOVING_PLATFORM, SWITCH, VENDOR, DONATION_VENDOR, ACHIEVEMENT_VENDOR, + PHANTOM_PHYSICS, SOUND_TRIGGER, RACING_SOUND_TRIGGER, BUFF, DESTROYABLE, COLLECTIBLE, PET, CHARACTER, INVENTORY, + SCRIPT, SKILL, BASE_COMBAT_AI, ITEM, QUICK_BUILD, MOVING_PLATFORM, SWITCH, VENDOR, DONATION_VENDOR, ACHIEVEMENT_VENDOR, BOUNCER, SCRIPTED_ACTIVITY, SHOOTING_GALLERY, RACING_CONTROL, LUP_EXHIBIT, MODEL, RENDER, MINI_GAME_CONTROL, }; }(); diff --git a/dGame/dComponents/ItemComponent.cpp b/dGame/dComponents/ItemComponent.cpp index 799cd935d..6d4969bc4 100644 --- a/dGame/dComponents/ItemComponent.cpp +++ b/dGame/dComponents/ItemComponent.cpp @@ -1,5 +1,12 @@ #include "ItemComponent.h" void ItemComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) { - outBitStream.Write0(); + // Live wrote the user-generated-content info on every construction of an object with only an item component: + // no UGC id, moderation status NoStatus, no description. + outBitStream.Write(isConstruction); + if (isConstruction) { + outBitStream.Write(LWOOBJID_EMPTY); // ug_id + outBitStream.Write(0); // ug_moderation_status NoStatus + outBitStream.Write0(); // ug_description + } } diff --git a/tests/dGameTests/dComponentsTests/ReplicaComponentOrderTests.cpp b/tests/dGameTests/dComponentsTests/ReplicaComponentOrderTests.cpp index 1995aec34..d9fad33aa 100644 --- a/tests/dGameTests/dComponentsTests/ReplicaComponentOrderTests.cpp +++ b/tests/dGameTests/dComponentsTests/ReplicaComponentOrderTests.cpp @@ -172,11 +172,6 @@ namespace { characterComponent->Serialize(outBitStream, bIsInitialUpdate); } - ItemComponent* itemComponent; - if (TryGet(entity, itemComponent)) { - itemComponent->Serialize(outBitStream, bIsInitialUpdate); - } - InventoryComponent* inventoryComponent; if (TryGet(entity, inventoryComponent)) { inventoryComponent->Serialize(outBitStream, bIsInitialUpdate); @@ -197,6 +192,14 @@ namespace { baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate); } + // The item comes after the skill and combat AI components, as the client reads them (ComponentOrderVector::Initialize + // 0x0101f8e0; live constructions of LOT 14535). It used to come before the inventory, which was harmless while + // it wrote a single 0 bit. + ItemComponent* itemComponent; + if (TryGet(entity, itemComponent)) { + itemComponent->Serialize(outBitStream, bIsInitialUpdate); + } + QuickBuildComponent* quickBuildComponent; if (TryGet(entity, quickBuildComponent)) { DestroyableComponent* destroyableComponent; diff --git a/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp b/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp index 96a650939..9f0fa6e9d 100644 --- a/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp +++ b/tests/dGameTests/dComponentsTests/ReplicaConstructionTests.cpp @@ -242,6 +242,25 @@ TEST_F(ReplicaConstructionTest, TemplateFactionMinusOneIsReplicated) { EXPECT_EQ(smashableDestroyable->GetFactionIDs(), std::vector{ 6 }); } +// An object with an item component (and no model component): live wrote the UGC info on every construction (99 of 99 +// constructions of LOT 14535): ug_id 0, NoStatus, no description. DLU wrote none. +TEST_F(ReplicaConstructionTest, ItemConstructionWritesEmptyUgcInfo) { + Entity item(15, info); + auto* const component = item.AddComponent(-1); + RakNet::BitStream construction; + component->Serialize(construction, true); + RakNet::BitStream expected; + expected.Write1(); + expected.Write(0); + expected.Write(0); + expected.Write0(); + ExpectSameBits(construction, expected); + + RakNet::BitStream serialization; + component->Serialize(serialization, false); + EXPECT_EQ(serialization.GetNumberOfBitsUsed(), 1u); +} + class InventoryConstructionTest : public GameDependenciesTest { protected: // The items of the live sample below (CDClient 1.10.64 values)