fix: write model behaviors only for property and inventory models

The client makes the mutable model component, which reads the behaviors and editing info, only when the object's config has propertyObjectID or inInventory (ObjectLoader2::LoadModelBehaviorsComponent); every other model gets the plain one, which reads only the model block. Writing the behaviors for every model made the client read them as the next component's data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-10-16 20:09:54 -05:00
parent 8ac72d2804
commit 13be051af9
2 changed files with 60 additions and 0 deletions

View File

@@ -156,6 +156,11 @@ void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialU
outBitStream.Write(m_OriginalPosition); // Original position
outBitStream.Write(m_OriginalRotation); // Original rotation
// The client makes the mutable model component, which reads the behaviors and editing info, only for objects whose
// config (the settings sent with a model) has propertyObjectID or inInventory; the plain one it makes for every
// other model reads only the block above, as live wrote it
if (!m_Parent->HasVar(u"propertyObjectID") && !m_Parent->GetVar<bool>(u"inInventory")) return;
outBitStream.Write1(); // We are writing behavior info
outBitStream.Write<uint32_t>(m_Behaviors.size()); // Number of behaviors
outBitStream.Write(m_IsPaused); // Is this model paused

View File

@@ -0,0 +1,55 @@
#include "GameDependencies.h"
#include "BitStream.h"
#include "CDClientManager.h"
#include "CDComponentsRegistryTable.h"
#include "Entity.h"
#include "GameMessages.h"
#include "ModelComponent.h"
#include <gtest/gtest.h>
class ModelComponentTests : public GameDependenciesTest {
protected:
void SetUp() override {
SetUpDependencies();
CDClientManager::GetEntriesMutable<CDComponentsRegistryTable>().insert_or_assign(static_cast<uint64_t>(info.lot), 0);
}
void TearDown() override {
TearDownDependencies();
}
// The bits a model's serialization writes
static uint32_t SerializedBits(Entity& entity) {
auto* model = entity.AddComponent<ModelComponent>(-1);
RakNet::BitStream bitStream;
model->Serialize(bitStream, true);
return bitStream.GetNumberOfBitsUsed();
}
};
// Item block (1 + 64 + 32 + 1), model block (1 + 1 + 32 + 96 + 128)
constexpr uint32_t ModelBlockBits = 98 + 258;
// Behavior info: flag, count, paused, and on the initial update the editing info flag
constexpr uint32_t BehaviorBits = 1 + 32 + 1 + 1;
// The client reads the behaviors only for property and inventory models (ObjectLoader2::LoadModelBehaviorsComponent)
TEST_F(ModelComponentTests, OtherModelsHaveNoBehaviorInfo) {
Entity entity(1, info);
EXPECT_EQ(SerializedBits(entity), ModelBlockBits);
}
TEST_F(ModelComponentTests, PropertyModelsHaveBehaviorInfo) {
EntityInfo propertyInfo = info;
propertyInfo.settings.Insert<bool>(u"propertyObjectID", true);
Entity entity(1, propertyInfo);
EXPECT_EQ(SerializedBits(entity), ModelBlockBits + BehaviorBits);
}
TEST_F(ModelComponentTests, InventoryModelsHaveBehaviorInfo) {
EntityInfo inventoryInfo = info;
inventoryInfo.settings.Insert<bool>(u"inInventory", true);
Entity entity(1, inventoryInfo);
EXPECT_EQ(SerializedBits(entity), ModelBlockBits + BehaviorBits);
}