From b4edfe2390d5821d13e76aae5e9c77af8bc3324a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 25 Feb 2016 12:06:22 -0800 Subject: [PATCH] no expiry for simulation ownership --- .../src/entities/EntityServer.cpp | 11 +- assignment-client/src/entities/EntityServer.h | 8 +- .../entities/src/SimpleEntitySimulation.cpp | 122 ++++++++++++------ .../entities/src/SimpleEntitySimulation.h | 7 +- libraries/physics/src/EntityMotionState.cpp | 23 ++-- 5 files changed, 106 insertions(+), 65 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index b6223497e6..b177d2a9a0 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -277,14 +277,17 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio // set of stats to have, but we'd probably want a different data structure if we keep it very long. // Since this version uses a single shared QMap for all senders, there could be some lock contention // on this QWriteLocker -void EntityServer::trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) { +void EntityServer::trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& sessionID) { QWriteLocker locker(&_viewerSendingStatsLock); - _viewerSendingStats[viewerNode][dataID] = { usecTimestampNow(), dataLastEdited }; + _viewerSendingStats[sessionID][dataID] = { usecTimestampNow(), dataLastEdited }; } -void EntityServer::trackViewerGone(const QUuid& viewerNode) { +void EntityServer::trackViewerGone(const QUuid& sessionID) { QWriteLocker locker(&_viewerSendingStatsLock); - _viewerSendingStats.remove(viewerNode); + _viewerSendingStats.remove(sessionID); + if (_entitySimulation) { + _entitySimulation->clearOwnership(sessionID); + } } QString EntityServer::serverSubclassStats() { diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index cd603f44d8..74057bfa5d 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -27,6 +27,8 @@ struct ViewerSendingStats { quint64 lastEdited; }; +class SimpleEntitySimulation; + class EntityServer : public OctreeServer, public NewlyCreatedEntityHook { Q_OBJECT public: @@ -52,8 +54,8 @@ public: virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override; virtual QString serverSubclassStats() override; - virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) override; - virtual void trackViewerGone(const QUuid& viewerNode) override; + virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& sessionID) override; + virtual void trackViewerGone(const QUuid& sessionID) override; public slots: void pruneDeletedEntities(); @@ -65,7 +67,7 @@ private slots: void handleEntityPacket(QSharedPointer message, SharedNodePointer senderNode); private: - EntitySimulation* _entitySimulation; + SimpleEntitySimulation* _entitySimulation; QTimer* _pruneDeletedEntitiesTimer = nullptr; QReadWriteLock _viewerSendingStatsLock; diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index bdf27f4440..693d80b27d 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -18,51 +18,69 @@ #include "EntityItem.h" #include "EntitiesLogging.h" -const quint64 MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD = 2 * USECS_PER_SECOND; - -void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { - if (_entitiesWithSimulator.size() == 0) { - return; - } - - if (now < _nextSimulationExpiry) { - // nothing has expired yet - return; - } - - // If an Entity has a simulation owner but there has been no update for a while: clear the owner. - // If an Entity goes ownerless for too long: zero velocity and remove from _entitiesWithSimulator. - _nextSimulationExpiry = now + MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD; +const quint64 MAX_OWNERLESS_PERIOD = 2 * USECS_PER_SECOND; +void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) { QMutexLocker lock(&_mutex); - SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin(); - while (itemItr != _entitiesWithSimulator.end()) { + SetOfEntities::iterator itemItr = _entitiesWithSimulationOwner.begin(); + while (itemItr != _entitiesWithSimulationOwner.end()) { EntityItemPointer entity = *itemItr; - quint64 expiry = entity->getLastChangedOnServer() + MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD; - if (expiry < now) { - if (entity->getSimulatorID().isNull()) { - // no simulators are volunteering - // zero the velocity on this entity so that it doesn't drift far away - entity->setVelocity(Vectors::ZERO); - entity->setAngularVelocity(Vectors::ZERO); - entity->setAcceleration(Vectors::ZERO); - // remove from list - itemItr = _entitiesWithSimulator.erase(itemItr); - continue; - } else { - // the simulator has stopped updating this object - // clear ownership and restart timer, giving nearby simulators time to volunteer - qCDebug(entities) << "auto-removing simulation owner " << entity->getSimulatorID(); - entity->clearSimulationOwnership(); + if (entity->getSimulatorID() == ownerID) { + // the simulator has abandonded this object --> remove from owned list + qCDebug(entities) << "auto-removing simulation owner " << entity->getSimulatorID(); + itemItr = _entitiesWithSimulationOwner.erase(itemItr); + + if (entity->getDynamic() && entity->hasLocalVelocity()) { + // it is still moving dynamically --> add to orphaned list + _entitiesThatNeedSimulationOwner.insert(entity); + quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD; + if (expiry < _nextOwnerlessExpiry) { + _nextOwnerlessExpiry = expiry; + } } + + // remove ownership and dirty all the tree elements that contain the it + entity->clearSimulationOwnership(); entity->markAsChangedOnServer(); - // dirty all the tree elements that contain the entity DirtyOctreeElementOperator op(entity->getElement()); getEntityTree()->recurseTreeWithOperator(&op); - } else if (expiry < _nextSimulationExpiry) { - _nextSimulationExpiry = expiry; + } else { + ++itemItr; + } + } +} + +void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { + if (now > _nextOwnerlessExpiry) { + // search for ownerless objects that have expired + QMutexLocker lock(&_mutex); + _nextOwnerlessExpiry = -1; + SetOfEntities::iterator itemItr = _entitiesThatNeedSimulationOwner.begin(); + while (itemItr != _entitiesThatNeedSimulationOwner.end()) { + EntityItemPointer entity = *itemItr; + quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD; + if (expiry < now) { + // no simulators have volunteered ownership --> remove from list + itemItr = _entitiesThatNeedSimulationOwner.erase(itemItr); + + if (entity->getSimulatorID().isNull() && entity->getDynamic() && entity->hasLocalVelocity()) { + // zero the derivatives + entity->setVelocity(Vectors::ZERO); + entity->setAngularVelocity(Vectors::ZERO); + entity->setAcceleration(Vectors::ZERO); + + // dirty all the tree elements that contain it + entity->markAsChangedOnServer(); + DirtyOctreeElementOperator op(entity->getElement()); + getEntityTree()->recurseTreeWithOperator(&op); + } + } else { + ++itemItr; + if (expiry < _nextOwnerlessExpiry) { + _nextOwnerlessExpiry = expiry; + } + } } - ++itemItr; } } @@ -70,26 +88,46 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { EntitySimulation::addEntityInternal(entity); if (!entity->getSimulatorID().isNull()) { QMutexLocker lock(&_mutex); - _entitiesWithSimulator.insert(entity); + _entitiesWithSimulationOwner.insert(entity); + } else if (entity->getDynamic() && entity->hasLocalVelocity()) { + QMutexLocker lock(&_mutex); + _entitiesThatNeedSimulationOwner.insert(entity); + quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD; + if (expiry < _nextOwnerlessExpiry) { + _nextOwnerlessExpiry = expiry; + } } } void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) { EntitySimulation::removeEntityInternal(entity); QMutexLocker lock(&_mutex); - _entitiesWithSimulator.remove(entity); + _entitiesWithSimulationOwner.remove(entity); + _entitiesThatNeedSimulationOwner.remove(entity); } void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) { EntitySimulation::changeEntityInternal(entity); - if (!entity->getSimulatorID().isNull()) { + if (entity->getSimulatorID().isNull()) { QMutexLocker lock(&_mutex); - _entitiesWithSimulator.insert(entity); + _entitiesWithSimulationOwner.remove(entity); + if (entity->getDynamic() && entity->hasLocalVelocity()) { + _entitiesThatNeedSimulationOwner.insert(entity); + quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD; + if (expiry < _nextOwnerlessExpiry) { + _nextOwnerlessExpiry = expiry; + } + } + } else { + QMutexLocker lock(&_mutex); + _entitiesWithSimulationOwner.insert(entity); + _entitiesThatNeedSimulationOwner.remove(entity); } entity->clearDirtyFlags(); } void SimpleEntitySimulation::clearEntitiesInternal() { - _entitiesWithSimulator.clear(); + _entitiesWithSimulationOwner.clear(); + _entitiesThatNeedSimulationOwner.clear(); } diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 53a7574bf2..d9c04fdcf9 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -21,6 +21,8 @@ public: SimpleEntitySimulation() : EntitySimulation() { } virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); } + void clearOwnership(const QUuid& ownerID); + protected: virtual void updateEntitiesInternal(const quint64& now) override; virtual void addEntityInternal(EntityItemPointer entity) override; @@ -28,8 +30,9 @@ protected: virtual void changeEntityInternal(EntityItemPointer entity) override; virtual void clearEntitiesInternal() override; - SetOfEntities _entitiesWithSimulator; - quint64 _nextSimulationExpiry { 0 }; + SetOfEntities _entitiesWithSimulationOwner; + SetOfEntities _entitiesThatNeedSimulationOwner; + quint64 _nextOwnerlessExpiry { 0 }; }; #endif // hifi_SimpleEntitySimulation_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8c0dab98db..6a818a1972 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -272,7 +272,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP; if (_numInactiveUpdates > 0) { - const uint8_t MAX_NUM_INACTIVE_UPDATES = 3; + const uint8_t MAX_NUM_INACTIVE_UPDATES = 20; if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) { // clear local ownership (stop sending updates) and let the server clear itself _entity->clearSimulationOwnership(); @@ -282,7 +282,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { // until it is removed from the outgoing updates // (which happens when we don't own the simulation and it isn't touching our simulation) const float INACTIVE_UPDATE_PERIOD = 0.5f; - return (dt > INACTIVE_UPDATE_PERIOD); + return (dt > INACTIVE_UPDATE_PERIOD * (float)_numInactiveUpdates); } if (!_body->isActive()) { @@ -404,8 +404,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q assert(_entity); assert(entityTreeIsLocked()); - bool active = _body->isActive(); - if (!active) { + if (!_body->isActive()) { // make sure all derivatives are zero glm::vec3 zero(0.0f); _entity->setVelocity(zero); @@ -495,16 +494,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q qCDebug(physics) << " lastSimulated:" << debugTime(lastSimulated, now); #endif //def WANT_DEBUG - if (sessionID == _entity->getSimulatorID()) { - // we think we own the simulation - 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.clearSimulationOwner(); - _outgoingPriority = ZERO_SIMULATION_PRIORITY; - } - // else the ownership is not changing so we don't bother to pack it - } else { + if (_numInactiveUpdates > 0) { + // 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.clearSimulationOwner(); + _outgoingPriority = ZERO_SIMULATION_PRIORITY; + } else if (sessionID != _entity->getSimulatorID()) { // we don't own the simulation for this entity yet, but we're sending a bid for it properties.setSimulationOwner(sessionID, glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY)); _nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;