From f882d971ff180f73d59be0c0294ced0bd52c199d Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Sun, 27 Sep 2026 04:23:42 -0500 Subject: [PATCH] fix(property): only the owner edits their property PropertyEditorBegin/End, UpdateModelFromClient and DeleteModelFromClient worked on whatever property the world had, for whoever sent them (and crashed on a world without one): a visitor could make the property private, send the other visitors away, or place and pick up the owner's models from the owner's inventory. They now need the property and its owner as the sender. PlacePropertyModel is only a notice (the client sends it with no model before UpdateModelFromClient) and no longer tries to place model 0. Co-Authored-By: Claude Opus 5.5 --- dGame/dGameMessages/PropertyMessages.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/dGame/dGameMessages/PropertyMessages.cpp b/dGame/dGameMessages/PropertyMessages.cpp index fee301d4b..d79fc1bf5 100644 --- a/dGame/dGameMessages/PropertyMessages.cpp +++ b/dGame/dGameMessages/PropertyMessages.cpp @@ -405,13 +405,17 @@ namespace GameMessages { } void PropertyEditorBegin::Handle(Entity& entity, const SystemAddress& sysAddr) { - PropertyManagementComponent::Instance()->OnStartBuilding(); + auto* property = PropertyManagementComponent::Instance(); + if (!property || property->GetOwnerId() != entity.GetObjectID()) return; + property->OnStartBuilding(); Game::zoneManager->GetZoneControlObject()->OnZonePropertyEditBegin(); } void PropertyEditorEnd::Handle(Entity& entity, const SystemAddress& sysAddr) { - PropertyManagementComponent::Instance()->OnFinishBuilding(); + auto* property = PropertyManagementComponent::Instance(); + if (!property || property->GetOwnerId() != entity.GetObjectID()) return; + property->OnFinishBuilding(); Game::zoneManager->GetZoneControlObject()->OnZonePropertyEditEnd(); } @@ -479,7 +483,9 @@ namespace GameMessages { } void PlacePropertyModel::Handle(Entity& entity, const SystemAddress& sysAddr) { - PropertyManagementComponent::Instance()->UpdateModelPosition(modelID, NiPoint3Constant::ZERO, QuatUtils::IDENTITY); + // The client sends this with no model and then places the model with UpdateModelFromClient (live capture); + // there is nothing to do here. + LOG_DEBUG("PlacePropertyModel (%llu) from %llu", modelID, entity.GetObjectID()); } void UpdateModelFromClient::Serialize(RakNet::BitStream& bitStream) const { @@ -496,7 +502,10 @@ namespace GameMessages { } void UpdateModelFromClient::Handle(Entity& entity, const SystemAddress& sysAddr) { - PropertyManagementComponent::Instance()->UpdateModelPosition(modelID, position, rotation); + auto* property = PropertyManagementComponent::Instance(); + // Only the owner edits their property; the model comes from the owner's inventory + if (!property || property->GetOwnerId() != entity.GetObjectID()) return; + property->UpdateModelPosition(modelID, position, rotation); } void DeleteModelFromClient::Serialize(RakNet::BitStream& bitStream) const { @@ -511,7 +520,9 @@ namespace GameMessages { } void DeleteModelFromClient::Handle(Entity& entity, const SystemAddress& sysAddr) { - PropertyManagementComponent::Instance()->DeleteModel(modelID, reason); + auto* property = PropertyManagementComponent::Instance(); + if (!property || property->GetOwnerId() != entity.GetObjectID()) return; + property->DeleteModel(modelID, reason); } void PropertyEntranceSync::Serialize(RakNet::BitStream& bitStream) const {