diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1e10ff2e48..b3c7f47b4f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -27,7 +27,8 @@ #include "EntityTree.h" #include "EntitySimulation.h" -const quint64 SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND); +const quint64 DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND); +const quint64 MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD = 2 * USECS_PER_SECOND; bool EntityItem::_sendPhysicsUpdates = true; @@ -322,6 +323,7 @@ int EntityItem::expectedBytes() { } +// clients use this method to unpack FULL updates from entity-server int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { @@ -596,24 +598,22 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // ownership has changed auto nodeList = DependencyManager::get(); if (_simulatorID == nodeList->getSessionUUID()) { - // we think we're the owner but entityServer says otherwise - // we relenquish ownership if the incoming priority is greater than or equal to ours + // we think we're the simulation owner but entity-server says otherwise + // we relenquish ownership iff the incoming priority is greater than or equal to ours // AND we don't have max priority if (priority >= _simulatorPriority && _simulatorPriority != MAX_SIMULATOR_PRIORITY) { // we're losing simulation ownership _simulatorID = id; _simulatorPriority = priority; - _simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD; } } else { _simulatorID = id; _simulatorPriority = priority; - _simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD; } } else if (priority != _simulatorPriority) { // priority is changing but simulatorID is not. // only accept this change if we are NOT the simulator owner, since otherwise - // we would have initiated this change + // we would have initiated this priority auto nodeList = DependencyManager::get(); if (_simulatorID != nodeList->getSessionUUID()) { _simulatorPriority = priority; @@ -1395,21 +1395,51 @@ void EntityItem::updateCreated(uint64_t value) { } } +void EntityItem::setSimulatorPriority(uint8_t priority) { + _simulatorPriority = priority; + if (_simulatorPriority == MAX_SIMULATOR_PRIORITY) { + // we always extend the the ownership expiry for MAX_SIMULATOR_PRIORITY + _simulationOwnershipExpiry = usecTimestampNow() + MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD; + } else if (_simulatorPriority == 0) { + _simulationOwnershipExpiry = 0; + } +} + void EntityItem::setSimulatorID(const QUuid& value) { if (_simulatorID != value) { _simulatorID = value; - _simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD; + if (!_simulatorID.isNull()) { + // Note: this logic only works well if _simulatorPriority is properly set before this point + quint64 lockoutPeriod = (_simulatorPriority == MAX_SIMULATOR_PRIORITY) + ? MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD : DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD; + _simulationOwnershipExpiry = usecTimestampNow() + lockoutPeriod; + } } } void EntityItem::updateSimulatorID(const QUuid& value) { if (_simulatorID != value) { _simulatorID = value; - _simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD; + if (!_simulatorID.isNull()) { + // Note: this logic only works well if _simulatorPriority is properly set before this point + quint64 lockoutPeriod = (_simulatorPriority == MAX_SIMULATOR_PRIORITY) + ? MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD : DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD; + _simulationOwnershipExpiry = usecTimestampNow() + lockoutPeriod; + } _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; } } +void EntityItem::clearSimulationOwnership() { + _simulatorPriority = 0; + _simulatorID = QUuid(); + _simulationOwnershipExpiry = 0; + // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulatorOwnership() + // is only ever called entity-server-side and the flags are only used client-side + //_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; + +} + bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) { assert(action); const QUuid& actionID = action->getID(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index fe82870992..eaae9949ec 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -60,8 +60,10 @@ const float ACTIVATION_LINEAR_VELOCITY_DELTA = 0.01f; const float ACTIVATION_GRAVITY_DELTA = 0.1f; const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f; -const uint8_t MIN_SIMULATOR_PRIORITY = 1; +const uint8_t VOLUNTEER_SIMULATOR_PRIORITY = 0x01; +const uint8_t SCRIPT_EDIT_SIMULATOR_PRIORITY = 0x80; const uint8_t MAX_SIMULATOR_PRIORITY = 0xff; +const uint8_t ATTACHMENT_SIMULATOR_PRIORITY = MAX_SIMULATOR_PRIORITY; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { }; @@ -320,13 +322,14 @@ public: const QString& getUserData() const { return _userData; } void setUserData(const QString& value) { _userData = value; } - void setSimulatorPriority(uint8_t priority) { _simulatorPriority = priority; } + void setSimulatorPriority(uint8_t priority); uint8_t getSimulatorPriority() const { return _simulatorPriority; } QUuid getSimulatorID() const { return _simulatorID; } void setSimulatorID(const QUuid& value); void updateSimulatorID(const QUuid& value); const quint64& getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; } + void clearSimulationOwnership(); const QString& getMarketplaceID() const { return _marketplaceID; } void setMarketplaceID(const QString& value) { _marketplaceID = value; } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index b33889189f..7b2fdf1098 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1234,3 +1234,17 @@ bool EntityItemProperties::hasTerseUpdateChanges() const { // a TerseUpdate includes the transform and its derivatives return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged; } + +void EntityItemProperties::clearSimulatorOwnership() { + _simulatorID = QUuid(); + _simulatorPriority = 0; + _simulatorIDChanged = true; + _simulatorPriorityChanged = true; +} + +void EntityItemProperties::setSimulatorOwnership(const QUuid& id, uint8_t priority) { + _simulatorID = id; + _simulatorPriority = glm::max(priority, _simulatorPriority); + _simulatorIDChanged = true; + _simulatorPriorityChanged = true; +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6f2aca8d92..230cd5c122 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -207,6 +207,9 @@ public: bool hasTerseUpdateChanges() const; + void clearSimulatorOwnership(); + void setSimulatorOwnership(const QUuid& id, uint8_t priority); + private: QUuid _id; bool _idSet; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c9f7378bc8..aaedf5a5e9 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -61,16 +61,6 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) { } } -void bidForSimulationOwnership(EntityItemProperties& properties) { - // We make a bid for simulation ownership by declaring our sessionID as simulation owner - // in the outgoing properties. The EntityServer may accept the bid or might not. - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); - properties.setSimulatorID(myNodeID); -} - - - QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { EntityItemProperties propertiesWithSimID = properties; @@ -83,11 +73,15 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties _entityTree->lockForWrite(); EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID); if (entity) { - entity->setLastBroadcast(usecTimestampNow()); // This Node is creating a new object. If it's in motion, set this Node as the simulator. - bidForSimulationOwnership(propertiesWithSimID); + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + propertiesWithSimID.setSimulatorOwnership(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY); + // and make note of it now, so we can act on it right away. entity->setSimulatorID(propertiesWithSimID.getSimulatorID()); + + entity->setLastBroadcast(usecTimestampNow()); } else { qCDebug(entities) << "script failed to add new Entity to local Octree"; success = false; @@ -160,9 +154,14 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties proper // balls" test. However, even if we solve this problem we still need to provide a "slerp the visible // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's // simulation. + + // we re-assert our simulation ownership + properties.setSimulatorOwnership(myNodeID, + glm::max(entity->getSimulatorPriority(), SCRIPT_EDIT_SIMULATOR_PRIORITY)); + } else { + // we make a bid for simulation ownership + properties.setSimulatorOwnership(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY); } - // we make a bid for (or assert existing) simulation ownership - properties.setSimulatorID(myNodeID); } entity->setLastBroadcast(usecTimestampNow()); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a3ef5f9261..ab981862a1 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -169,15 +169,12 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI // so we apply the rules for ownership change: // (1) higher priority wins // (2) equal priority wins if ownership filter has expired except... - // (3) max priority never loses uint8_t oldPriority = entity->getSimulatorPriority(); - if (oldPriority != MAX_SIMULATOR_PRIORITY) { - uint8_t newPriority = properties.getSimulatorPriority(); - if (newPriority > oldPriority || - (newPriority == oldPriority && - usecTimestampNow() > entity->getSimulationOwnershipExpiry())) { - simulationBlocked = false; - } + uint8_t newPriority = properties.getSimulatorPriority(); + if (newPriority > oldPriority || + (newPriority == oldPriority && + usecTimestampNow() > entity->getSimulationOwnershipExpiry())) { + simulationBlocked = false; } } } else { diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index 8dedbd2162..ff8d72b353 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -23,18 +23,18 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { // has finished simulating it. auto nodeList = DependencyManager::get(); - SetOfEntities::iterator itemItr = _hasSimulationOwnerEntities.begin(); - while (itemItr != _hasSimulationOwnerEntities.end()) { + SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin(); + while (itemItr != _entitiesWithSimulator.end()) { EntityItemPointer entity = *itemItr; if (entity->getSimulatorID().isNull()) { - itemItr = _hasSimulationOwnerEntities.erase(itemItr); + itemItr = _entitiesWithSimulator.erase(itemItr); } else if (now - entity->getLastChangedOnServer() >= AUTO_REMOVE_SIMULATION_OWNER_USEC) { SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID()); if (ownerNode.isNull() || !ownerNode->isAlive()) { qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID(); // TODO: zero velocities when we clear simulatorID? - entity->setSimulatorID(QUuid()); - itemItr = _hasSimulationOwnerEntities.erase(itemItr); + entity->clearSimulationOwnership(); + itemItr = _entitiesWithSimulator.erase(itemItr); } else { ++itemItr; } @@ -47,23 +47,23 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { EntitySimulation::addEntityInternal(entity); if (!entity->getSimulatorID().isNull()) { - _hasSimulationOwnerEntities.insert(entity); + _entitiesWithSimulator.insert(entity); } } void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) { - _hasSimulationOwnerEntities.remove(entity); + _entitiesWithSimulator.remove(entity); } void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) { EntitySimulation::changeEntityInternal(entity); if (!entity->getSimulatorID().isNull()) { - _hasSimulationOwnerEntities.insert(entity); + _entitiesWithSimulator.insert(entity); } entity->clearDirtyFlags(); } void SimpleEntitySimulation::clearEntitiesInternal() { - _hasSimulationOwnerEntities.clear(); + _entitiesWithSimulator.clear(); } diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 6eb3980dd3..fff0659067 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -28,7 +28,7 @@ protected: virtual void changeEntityInternal(EntityItemPointer entity); virtual void clearEntitiesInternal(); - SetOfEntities _hasSimulationOwnerEntities; + SetOfEntities _entitiesWithSimulator; }; #endif // hifi_SimpleEntitySimulation_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 5e9591a031..9a65bac042 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -97,7 +97,9 @@ void EntityMotionState::handleEasyChanges(uint32_t flags) { assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags); + if (flags & EntityItem::DIRTY_SIMULATOR_ID) { + _loopsSinceOwnershipBid = 0; _loopsWithoutOwner = 0; _candidateForOwnership = 0; if (_entity->getSimulatorID().isNull() @@ -453,14 +455,15 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q if (!active) { // we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID // but we remember that we do still own it... and rely on the server to tell us that we don't - properties.setSimulatorID(QUuid()); + properties.clearSimulatorOwnership(); } else { - // explicitly set the property's simulatorID so that it is flagged as changed and will be packed - properties.setSimulatorID(sessionID); + // re-assert the simulation info + properties.setSimulatorOwnership(sessionID, _entity->getSimulatorPriority()); } } else { // we don't own the simulation for this entity yet, but we're sending a bid for it - properties.setSimulatorID(sessionID); + properties.setSimulatorOwnership(sessionID, glm::max(_simulatorPriorityHint, VOLUNTEER_SIMULATOR_PRIORITY)); + _simulatorPriorityHint = 0; } if (EntityItem::getSendPhysicsUpdates()) { @@ -580,3 +583,8 @@ int16_t EntityMotionState::computeCollisionGroup() { } return COLLISION_GROUP_DEFAULT; } + +void EntityMotionState::setSimulatorPriorityHint(uint8_t priority) { + _candidateForOwnership = true; + _simulatorPriorityHint = priority; +} diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 4f777a4575..d9350311b1 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -80,6 +80,9 @@ public: virtual int16_t computeCollisionGroup(); + // eternal logic can suggest a simuator priority bid for the next outgoing update + void setSimulatorPriorityHint(uint8_t priority); + friend class PhysicalEntitySimulation; protected: @@ -114,6 +117,7 @@ protected: bool _candidateForOwnership; uint32_t _loopsSinceOwnershipBid; uint32_t _loopsWithoutOwner; + uint8_t _simulatorPriorityHint; }; #endif // hifi_EntityMotionState_h