bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& origProperties, EntityTreeElement* containingElement, const SharedNodePointer& senderNode) { EntityItemProperties properties = origProperties; bool allowLockChange; QUuid senderID; if (senderNode.isNull()) { auto nodeList = DependencyManager::get<NodeList>(); allowLockChange = nodeList->getThisNodeCanAdjustLocks(); senderID = nodeList->getSessionUUID(); } else { allowLockChange = senderNode->getCanAdjustLocks(); senderID = senderNode->getUUID(); } if (!allowLockChange && (entity->getLocked() != properties.getLocked())) { qCDebug(entities) << "Refusing disallowed lock adjustment."; return false; } // enforce support for locked entities. If an entity is currently locked, then the only // property we allow you to change is the locked property. if (entity->getLocked()) { if (properties.lockedChanged()) { bool wantsLocked = properties.getLocked(); if (!wantsLocked) { EntityItemProperties tempProperties; tempProperties.setLocked(wantsLocked); UpdateEntityOperator theOperator(this, containingElement, entity, tempProperties); recurseTreeWithOperator(&theOperator); _isDirty = true; } } } else { if (getIsServer()) { bool simulationBlocked = !entity->getSimulatorID().isNull(); if (properties.simulatorIDChanged()) { QUuid submittedID = properties.getSimulatorID(); // a legit interface will only submit their own ID or NULL: if (submittedID.isNull()) { if (entity->getSimulatorID() == senderID) { // We only allow the simulation owner to clear their own simulationID's. simulationBlocked = false; } // else: We assume the sender really did believe it was the simulation owner when it sent } else if (submittedID == senderID) { // the sender is trying to take or continue ownership if (entity->getSimulatorID().isNull() || entity->getSimulatorID() == senderID) { simulationBlocked = false; } else { // the sender is trying to steal ownership from another simulator // so we apply the ownership change filter if (usecTimestampNow() - entity->getSimulatorIDChangedTime() > SIMULATOR_CHANGE_LOCKOUT_PERIOD) { simulationBlocked = false; } } } else { // the entire update is suspect --> ignore it return false; } } if (simulationBlocked) { // squash the physics-related changes. properties.setSimulatorIDChanged(false); properties.setPositionChanged(false); properties.setRotationChanged(false); } } // else client accepts what the server says QString entityScriptBefore = entity->getScript(); QString collisionSoundURLBefore = entity->getCollisionSoundURL(); uint32_t preFlags = entity->getDirtyFlags(); UpdateEntityOperator theOperator(this, containingElement, entity, properties); recurseTreeWithOperator(&theOperator); _isDirty = true; uint32_t newFlags = entity->getDirtyFlags() & ~preFlags; if (newFlags) { if (_simulation) { if (newFlags & DIRTY_SIMULATION_FLAGS) { _simulation->lock(); _simulation->changeEntity(entity); _simulation->unlock(); } } else { // normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly entity->clearDirtyFlags(); } } QString entityScriptAfter = entity->getScript(); if (entityScriptBefore != entityScriptAfter) { emitEntityScriptChanging(entity->getEntityItemID()); // the entity script has changed } maybeNotifyNewCollisionSoundURL(collisionSoundURLBefore, entity->getCollisionSoundURL()); } // TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG). containingElement = getContainingElement(entity->getEntityItemID()); if (!containingElement) { qCDebug(entities) << "UNEXPECTED!!!! after updateEntity() we no longer have a containing element??? entityID=" << entity->getEntityItemID(); return false; } return true; }