mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix(entity): models get their item component, as the client makes it
The client makes the registry's item component for models too (only a pet takes it away); DLU skipped it and the model component wrote the item block itself, for the 30 model LOTs without an item row as well, and with the object id as ug_id. Live (Raven Bluff property capture): the item block carries the userModelID for brick built models (LOT 14) and 0 for reward models (LOT 8069). 1,455 LOTs gain the item component; nothing else changes (LoaderSnapshotTest). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -262,6 +262,8 @@ void Entity::Initialize() {
|
||||
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
|
||||
AddComponent<ItemComponent>(compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM));
|
||||
|
||||
AddComponent<ModelComponent>(-1)->LoadBehaviors();
|
||||
|
||||
AddComponent<RenderComponent>(-1);
|
||||
@@ -646,11 +648,11 @@ void Entity::Initialize() {
|
||||
break;
|
||||
}
|
||||
case eReplicaComponentType::ITEM: {
|
||||
// A pet takes the item (and model) component away (LWOPetComponent::Factory); models keep theirs (live: an
|
||||
// item block before the model's)
|
||||
PetComponent* petComponent{};
|
||||
const auto itemID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM);
|
||||
// The model (42) comes later in the client's order: a model without a pet has no item component
|
||||
const bool hasModel = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) > -1 && petComponentID <= 0;
|
||||
if (itemID > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !hasModel) {
|
||||
if (itemID > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent)) {
|
||||
AddComponent<ItemComponent>(itemID);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "ItemComponent.h"
|
||||
|
||||
void ItemComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
|
||||
// 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.
|
||||
// Live wrote the user-generated-content info on every construction: the model's userModelID (a brick built model,
|
||||
// LOT 14), else no UGC id (items, reward models such as LOT 8069), moderation status NoStatus, no description.
|
||||
outBitStream.Write(isConstruction);
|
||||
if (isConstruction) {
|
||||
outBitStream.Write<LWOOBJID>(LWOOBJID_EMPTY); // ug_id
|
||||
outBitStream.Write<LWOOBJID>(m_Parent->GetVarAs<LWOOBJID>(u"userModelID")); // ug_id
|
||||
outBitStream.Write<uint32_t>(0); // ug_moderation_status NoStatus
|
||||
outBitStream.Write0(); // ug_description
|
||||
}
|
||||
|
||||
@@ -131,14 +131,7 @@ void ModelComponent::Resume() {
|
||||
}
|
||||
|
||||
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||
// ItemComponent Serialization. Pets do not get this serialization.
|
||||
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
|
||||
outBitStream.Write1();
|
||||
outBitStream.Write<LWOOBJID>(m_userModelID != LWOOBJID_EMPTY ? m_userModelID : m_Parent->GetObjectID());
|
||||
outBitStream.Write<int>(0);
|
||||
outBitStream.Write0();
|
||||
}
|
||||
|
||||
// The item block in front of it is the ItemComponent's (the client makes one from the registry)
|
||||
//actual model component:
|
||||
outBitStream.Write1(); // Yes we are writing model info
|
||||
outBitStream.Write(m_NumListeningInteract > 0); // Is pickable
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "LevelProgressionComponent.h"
|
||||
#include "MessageIdentifiers.h"
|
||||
#include "ItemComponent.h"
|
||||
#include "ModelComponent.h"
|
||||
#include "PlayerForcedMovementComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
@@ -141,14 +142,14 @@ TEST_F(ReplicaDecoderTest, PlayerConstructionReadsTheCharacterParts) {
|
||||
EXPECT_EQ((*FieldsOf(*constructed, "CHARACTER"))["statistics"].size(), 27u);
|
||||
}
|
||||
|
||||
// A model's destroyable is not in its registry rows: it is written after the other components. Its item component is
|
||||
// in the rows but not made (Entity::Initialize): the model component writes the item info
|
||||
// A model's destroyable is not in its registry rows: it is written after the other components. Its item component comes
|
||||
// from the rows, in front of the model's block, with no UGC id when there is no userModelID (live: reward model LOT 8069)
|
||||
TEST_F(ReplicaDecoderTest, ModelWithTheDestroyableTheRegistryDoesNotList) {
|
||||
info.lot = 6000;
|
||||
Entity model(288300744895900200, info);
|
||||
auto* const physics = model.AddComponent<SimplePhysicsComponent>(-1);
|
||||
physics->SetPosition(NiPoint3(-12.0f, 432.96f, -49.6f));
|
||||
// The model component writes the item's user-generated-content block itself, where the client reads the item
|
||||
model.AddComponent<ItemComponent>(-1);
|
||||
model.AddComponent<ModelComponent>(-1)->LoadBehaviors();
|
||||
auto* const destroyable = model.AddComponent<DestroyableComponent>(-1);
|
||||
destroyable->SetIsSmashable(true);
|
||||
@@ -161,7 +162,25 @@ TEST_F(ReplicaDecoderTest, ModelWithTheDestroyableTheRegistryDoesNotList) {
|
||||
EXPECT_EQ(Names(*constructed), "SIMPLE_PHYSICS ITEM MODEL DESTROYABLE ");
|
||||
EXPECT_EQ((*FieldsOf(*constructed, "DESTROYABLE"))["smashable"], true);
|
||||
EXPECT_EQ((*FieldsOf(*constructed, "SIMPLE_PHYSICS"))["position"], json::array({ -12.0f, 432.96f, -49.6f }));
|
||||
EXPECT_EQ((*FieldsOf(*constructed, "ITEM"))["ugID"], "288300744895900200");
|
||||
EXPECT_EQ((*FieldsOf(*constructed, "ITEM"))["ugID"], "0");
|
||||
}
|
||||
|
||||
// A brick built model's item block carries its userModelID (live: LOT 14 on a Raven Bluff property, ug_id
|
||||
// 1152921507179669081, item block between the simple physics and the model)
|
||||
TEST_F(ReplicaDecoderTest, BrickModelItemHasItsUserModelId) {
|
||||
info.lot = 14;
|
||||
info.settings.Insert<LWOOBJID>(u"userModelID", 1152921507179669081);
|
||||
Entity model(288300744895900201, info);
|
||||
info.settings.values.clear();
|
||||
model.AddComponent<SimplePhysicsComponent>(-1);
|
||||
model.AddComponent<ItemComponent>(-1);
|
||||
model.AddComponent<ModelComponent>(-1)->LoadBehaviors();
|
||||
const ReplicaDecoder::ComponentTable table{ { 14, { SIMPLE_PHYSICS, ITEM, MODEL } } };
|
||||
ReplicaDecoder::Session session(table);
|
||||
const auto constructed = session.Decode(Construction(model, 5), 1);
|
||||
ASSERT_TRUE(constructed);
|
||||
EXPECT_FALSE(constructed->contains("(layout did not match)")) << constructed->dump();
|
||||
EXPECT_EQ((*FieldsOf(*constructed, "ITEM"))["ugID"], "1152921507179669081");
|
||||
}
|
||||
|
||||
// When no layout reads the packet exactly, what read is shown with the rest as bits, never a guess
|
||||
@@ -359,6 +378,7 @@ TEST_F(ReplicaDecoderTest, ModelBehaviorsOnlyOnPropertyModels) {
|
||||
if (onProperty) info.settings.Insert<bool>(u"propertyObjectID", true);
|
||||
Entity model(288300744895900210, info);
|
||||
model.AddComponent<SimplePhysicsComponent>(-1);
|
||||
model.AddComponent<ItemComponent>(-1);
|
||||
model.AddComponent<ModelComponent>(-1)->LoadBehaviors();
|
||||
ReplicaDecoder::Session session(table);
|
||||
const auto constructed = session.Decode(Construction(model, 4), 1);
|
||||
|
||||
Reference in New Issue
Block a user