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 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 04:23:42 -05:00
parent 68a810ea7b
commit f882d971ff

View File

@@ -405,13 +405,17 @@ namespace GameMessages {
} }
void PropertyEditorBegin::Handle(Entity& entity, const SystemAddress& sysAddr) { 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(); Game::zoneManager->GetZoneControlObject()->OnZonePropertyEditBegin();
} }
void PropertyEditorEnd::Handle(Entity& entity, const SystemAddress& sysAddr) { 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(); Game::zoneManager->GetZoneControlObject()->OnZonePropertyEditEnd();
} }
@@ -479,7 +483,9 @@ namespace GameMessages {
} }
void PlacePropertyModel::Handle(Entity& entity, const SystemAddress& sysAddr) { 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 { void UpdateModelFromClient::Serialize(RakNet::BitStream& bitStream) const {
@@ -496,7 +502,10 @@ namespace GameMessages {
} }
void UpdateModelFromClient::Handle(Entity& entity, const SystemAddress& sysAddr) { 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 { void DeleteModelFromClient::Serialize(RakNet::BitStream& bitStream) const {
@@ -511,7 +520,9 @@ namespace GameMessages {
} }
void DeleteModelFromClient::Handle(Entity& entity, const SystemAddress& sysAddr) { 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 { void PropertyEntranceSync::Serialize(RakNet::BitStream& bitStream) const {