Merge pull request #12773 from AndrewMeadows/expire-simulation-ownership

entity-server will expire stale simulation ownership
This commit is contained in:
John Conklin II 2018-04-12 12:04:52 -07:00 committed by GitHub
commit e47dca77ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 105 additions and 68 deletions

View file

@ -311,7 +311,8 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get()); _entitiesInQueue.insert(entity.get());
} }
} else if (entity->getLastEdited() > knownTimestamp->second) { } else if (entity->getLastEdited() > knownTimestamp->second
|| entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority // it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly // TODO: sort these correctly
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
@ -330,7 +331,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
return; return;
} }
auto knownTimestamp = _knownState.find(entity.get()); auto knownTimestamp = _knownState.find(entity.get());
if (knownTimestamp == _knownState.end() || entity->getLastEdited() > knownTimestamp->second) { if (knownTimestamp == _knownState.end()
|| entity->getLastEdited() > knownTimestamp->second
|| entity->getLastChangedOnServer() > knownTimestamp->second) {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get()); _entitiesInQueue.insert(entity.get());
} }
@ -382,7 +385,8 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_entitiesInQueue.insert(entity.get()); _entitiesInQueue.insert(entity.get());
} }
} else if (entity->getLastEdited() > knownTimestamp->second) { } else if (entity->getLastEdited() > knownTimestamp->second
|| entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority // it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly // TODO: sort these correctly
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));

View file

@ -693,7 +693,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// the entity-server is awarding us ownership which is what we want // the entity-server is awarding us ownership which is what we want
_simulationOwner.set(newSimOwner); _simulationOwner.set(newSimOwner);
} }
} else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) { } else if (newSimOwner.matchesValidID(myNodeID) && !_simulationOwner.pendingTake(now)) {
// entity-server tells us that we have simulation ownership while we never requested this for this EntityItem, // entity-server tells us that we have simulation ownership while we never requested this for this EntityItem,
// this could happen when the user reloads the cache and entity tree. // this could happen when the user reloads the cache and entity tree.
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
@ -1946,10 +1946,6 @@ void EntityItem::setPendingOwnershipPriority(uint8_t priority, const quint64& ti
_simulationOwner.setPendingPriority(priority, timestamp); _simulationOwner.setPendingPriority(priority, timestamp);
} }
void EntityItem::rememberHasSimulationOwnershipBid() const {
_hasBidOnSimulation = true;
}
QString EntityItem::actionsToDebugString() { QString EntityItem::actionsToDebugString() {
QString result; QString result;
QVector<QByteArray> serializedActions; QVector<QByteArray> serializedActions;

View file

@ -58,6 +58,9 @@ using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElemen
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10)) #define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
#define debugTreeVector(V) V << "[" << V << " in meters ]" #define debugTreeVector(V) V << "[" << V << " in meters ]"
const uint64_t MAX_OUTGOING_SIMULATION_UPDATE_PERIOD = 9 * USECS_PER_SECOND;
const uint64_t MAX_INCOMING_SIMULATION_UPDATE_PERIOD = MAX_OUTGOING_SIMULATION_UPDATE_PERIOD + USECS_PER_SECOND;
class MeshProxyList; class MeshProxyList;
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
@ -489,6 +492,9 @@ public:
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
std::unordered_map<std::string, graphics::MultiMaterial> getMaterials(); std::unordered_map<std::string, graphics::MultiMaterial> getMaterials();
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
signals: signals:
void requestRenderUpdate(); void requestRenderUpdate();
@ -619,9 +625,6 @@ protected:
static quint64 _rememberDeletedActionTime; static quint64 _rememberDeletedActionTime;
mutable QHash<QUuid, quint64> _previouslyDeletedActions; mutable QHash<QUuid, quint64> _previouslyDeletedActions;
// per entity keep state if it ever bid on simulation, so that we can ignore false simulation ownership
mutable bool _hasBidOnSimulation { false };
QUuid _sourceUUID; /// the server node UUID we came from QUuid _sourceUUID; /// the server node UUID we came from
bool _clientOnly { false }; bool _clientOnly { false };
@ -642,6 +645,7 @@ protected:
quint64 _lastUpdatedAngularVelocityTimestamp { 0 }; quint64 _lastUpdatedAngularVelocityTimestamp { 0 };
quint64 _lastUpdatedAccelerationTimestamp { 0 }; quint64 _lastUpdatedAccelerationTimestamp { 0 };
quint64 _lastUpdatedQueryAACubeTimestamp { 0 }; quint64 _lastUpdatedQueryAACubeTimestamp { 0 };
uint64_t _simulationOwnershipExpiry { 0 };
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera

View file

@ -454,7 +454,6 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
// we make a bid for simulation ownership // we make a bid for simulation ownership
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY); entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
entity->rememberHasSimulationOwnershipBid();
} }
} }
if (properties.queryAACubeRelatedPropertyChanged()) { if (properties.queryAACubeRelatedPropertyChanged()) {

View file

@ -370,12 +370,18 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
simulationBlocked = false; simulationBlocked = false;
} }
} }
if (!simulationBlocked) {
entity->setSimulationOwnershipExpiry(usecTimestampNow() + MAX_INCOMING_SIMULATION_UPDATE_PERIOD);
}
} else { } else {
// the entire update is suspect --> ignore it // the entire update is suspect --> ignore it
return false; return false;
} }
} else if (simulationBlocked) { } else if (simulationBlocked) {
simulationBlocked = senderID != entity->getSimulatorID(); simulationBlocked = senderID != entity->getSimulatorID();
if (!simulationBlocked) {
entity->setSimulationOwnershipExpiry(usecTimestampNow() + MAX_INCOMING_SIMULATION_UPDATE_PERIOD);
}
} }
if (simulationBlocked) { if (simulationBlocked) {
// squash ownership and physics-related changes. // squash ownership and physics-related changes.

View file

@ -243,14 +243,10 @@ public:
return std::static_pointer_cast<const OctreeElement>(shared_from_this()); return std::static_pointer_cast<const OctreeElement>(shared_from_this());
} }
void bumpChangedContent() { _lastChangedContent = usecTimestampNow(); }
uint64_t getLastChangedContent() const { return _lastChangedContent; }
protected: protected:
virtual void init(unsigned char * octalCode) override; virtual void init(unsigned char * octalCode) override;
EntityTreePointer _myTree; EntityTreePointer _myTree;
EntityItems _entityItems; EntityItems _entityItems;
uint64_t _lastChangedContent { 0 };
}; };
#endif // hifi_EntityTreeElement_h #endif // hifi_EntityTreeElement_h

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
//#include <PerfStat.h>
#include "SimpleEntitySimulation.h" #include "SimpleEntitySimulation.h"
#include <DirtyOctreeElementOperator.h> #include <DirtyOctreeElementOperator.h>
@ -27,16 +25,13 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
EntityItemPointer entity = *itemItr; EntityItemPointer entity = *itemItr;
if (entity->getSimulatorID() == ownerID) { if (entity->getSimulatorID() == ownerID) {
// the simulator has abandonded this object --> remove from owned list // the simulator has abandonded this object --> remove from owned list
qCDebug(entities) << "auto-removing simulation owner " << entity->getSimulatorID();
itemItr = _entitiesWithSimulationOwner.erase(itemItr); itemItr = _entitiesWithSimulationOwner.erase(itemItr);
if (entity->getDynamic() && entity->hasLocalVelocity()) { if (entity->getDynamic() && entity->hasLocalVelocity()) {
// it is still moving dynamically --> add to orphaned list // it is still moving dynamically --> add to orphaned list
_entitiesThatNeedSimulationOwner.insert(entity); _entitiesThatNeedSimulationOwner.insert(entity);
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD; uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
if (expiry < _nextOwnerlessExpiry) { _nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
_nextOwnerlessExpiry = expiry;
}
} }
// remove ownership and dirty all the tree elements that contain the it // remove ownership and dirty all the tree elements that contain the it
@ -51,37 +46,8 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
} }
void SimpleEntitySimulation::updateEntitiesInternal(uint64_t now) { void SimpleEntitySimulation::updateEntitiesInternal(uint64_t now) {
if (now > _nextOwnerlessExpiry) { expireStaleOwnerships(now);
// search for ownerless objects that have expired stopOwnerlessEntities(now);
QMutexLocker lock(&_mutex);
_nextOwnerlessExpiry = std::numeric_limits<uint64_t>::max();
SetOfEntities::iterator itemItr = _entitiesThatNeedSimulationOwner.begin();
while (itemItr != _entitiesThatNeedSimulationOwner.end()) {
EntityItemPointer entity = *itemItr;
uint64_t 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;
}
}
}
}
} }
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) { void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
@ -93,13 +59,12 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
if (!entity->getSimulatorID().isNull()) { if (!entity->getSimulatorID().isNull()) {
QMutexLocker lock(&_mutex); QMutexLocker lock(&_mutex);
_entitiesWithSimulationOwner.insert(entity); _entitiesWithSimulationOwner.insert(entity);
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
} else if (entity->getDynamic() && entity->hasLocalVelocity()) { } else if (entity->getDynamic() && entity->hasLocalVelocity()) {
QMutexLocker lock(&_mutex); QMutexLocker lock(&_mutex);
_entitiesThatNeedSimulationOwner.insert(entity); _entitiesThatNeedSimulationOwner.insert(entity);
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD; uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
if (expiry < _nextOwnerlessExpiry) { _nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
_nextOwnerlessExpiry = expiry;
}
} }
} }
@ -128,13 +93,12 @@ void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
if (entity->getDynamic() && entity->hasLocalVelocity()) { if (entity->getDynamic() && entity->hasLocalVelocity()) {
_entitiesThatNeedSimulationOwner.insert(entity); _entitiesThatNeedSimulationOwner.insert(entity);
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD; uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
if (expiry < _nextOwnerlessExpiry) { _nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
_nextOwnerlessExpiry = expiry;
}
} }
} else { } else {
QMutexLocker lock(&_mutex); QMutexLocker lock(&_mutex);
_entitiesWithSimulationOwner.insert(entity); _entitiesWithSimulationOwner.insert(entity);
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
_entitiesThatNeedSimulationOwner.remove(entity); _entitiesThatNeedSimulationOwner.remove(entity);
} }
entity->clearDirtyFlags(); entity->clearDirtyFlags();
@ -155,3 +119,58 @@ void SimpleEntitySimulation::sortEntitiesThatMoved() {
} }
EntitySimulation::sortEntitiesThatMoved(); EntitySimulation::sortEntitiesThatMoved();
} }
void SimpleEntitySimulation::expireStaleOwnerships(uint64_t now) {
if (now > _nextStaleOwnershipExpiry) {
_nextStaleOwnershipExpiry = (uint64_t)(-1);
SetOfEntities::iterator itemItr = _entitiesWithSimulationOwner.begin();
while (itemItr != _entitiesWithSimulationOwner.end()) {
EntityItemPointer entity = *itemItr;
uint64_t expiry = entity->getSimulationOwnershipExpiry();
if (now > expiry) {
itemItr = _entitiesWithSimulationOwner.erase(itemItr);
// remove ownership and dirty all the tree elements that contain the it
entity->clearSimulationOwnership();
entity->markAsChangedOnServer();
DirtyOctreeElementOperator op(entity->getElement());
getEntityTree()->recurseTreeWithOperator(&op);
} else {
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, expiry);
++itemItr;
}
}
}
}
void SimpleEntitySimulation::stopOwnerlessEntities(uint64_t now) {
if (now > _nextOwnerlessExpiry) {
// search for ownerless objects that have expired
QMutexLocker lock(&_mutex);
_nextOwnerlessExpiry = (uint64_t)(-1);
SetOfEntities::iterator itemItr = _entitiesThatNeedSimulationOwner.begin();
while (itemItr != _entitiesThatNeedSimulationOwner.end()) {
EntityItemPointer entity = *itemItr;
uint64_t 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 {
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
++itemItr;
}
}
}
}

View file

@ -23,22 +23,26 @@ using SimpleEntitySimulationPointer = std::shared_ptr<SimpleEntitySimulation>;
class SimpleEntitySimulation : public EntitySimulation { class SimpleEntitySimulation : public EntitySimulation {
public: public:
SimpleEntitySimulation() : EntitySimulation() { } SimpleEntitySimulation() : EntitySimulation() { }
virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); } ~SimpleEntitySimulation() { clearEntitiesInternal(); }
void clearOwnership(const QUuid& ownerID); void clearOwnership(const QUuid& ownerID);
protected: protected:
virtual void updateEntitiesInternal(uint64_t now) override; void updateEntitiesInternal(uint64_t now) override;
virtual void addEntityInternal(EntityItemPointer entity) override; void addEntityInternal(EntityItemPointer entity) override;
virtual void removeEntityInternal(EntityItemPointer entity) override; void removeEntityInternal(EntityItemPointer entity) override;
virtual void changeEntityInternal(EntityItemPointer entity) override; void changeEntityInternal(EntityItemPointer entity) override;
virtual void clearEntitiesInternal() override; void clearEntitiesInternal() override;
virtual void sortEntitiesThatMoved() override; void sortEntitiesThatMoved() override;
void expireStaleOwnerships(uint64_t now);
void stopOwnerlessEntities(uint64_t now);
SetOfEntities _entitiesWithSimulationOwner; SetOfEntities _entitiesWithSimulationOwner;
SetOfEntities _entitiesThatNeedSimulationOwner; SetOfEntities _entitiesThatNeedSimulationOwner;
uint64_t _nextOwnerlessExpiry { 0 }; uint64_t _nextOwnerlessExpiry { 0 };
uint64_t _nextStaleOwnershipExpiry { (uint64_t)(-1) };
}; };
#endif // hifi_SimpleEntitySimulation_h #endif // hifi_SimpleEntitySimulation_h

View file

@ -14,6 +14,7 @@
DirtyOctreeElementOperator::DirtyOctreeElementOperator(const OctreeElementPointer& element) DirtyOctreeElementOperator::DirtyOctreeElementOperator(const OctreeElementPointer& element)
: _element(element) { : _element(element) {
assert(_element.get()); assert(_element.get());
_element->bumpChangedContent();
_point = _element->getAACube().calcCenter(); _point = _element->getAACube().calcCenter();
} }

View file

@ -219,6 +219,9 @@ public:
int getMyChildContaining(const AABox& box) const; int getMyChildContaining(const AABox& box) const;
int getMyChildContainingPoint(const glm::vec3& point) const; int getMyChildContainingPoint(const glm::vec3& point) const;
void bumpChangedContent() { _lastChangedContent = usecTimestampNow(); }
uint64_t getLastChangedContent() const { return _lastChangedContent; }
protected: protected:
void deleteAllChildren(); void deleteAllChildren();
@ -235,6 +238,7 @@ protected:
} _octalCode; } _octalCode;
quint64 _lastChanged; /// Client and server, timestamp this node was last changed, 8 bytes quint64 _lastChanged; /// Client and server, timestamp this node was last changed, 8 bytes
uint64_t _lastChangedContent { 0 };
/// Client and server, pointers to child nodes, various encodings /// Client and server, pointers to child nodes, various encodings
#ifdef SIMPLE_CHILD_ARRAY #ifdef SIMPLE_CHILD_ARRAY

View file

@ -365,6 +365,11 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
return true; return true;
} }
if (usecTimestampNow() > _entity->getSimulationOwnershipExpiry()) {
// send update every so often else server will revoke our ownership
return true;
}
_lastStep = simulationStep; _lastStep = simulationStep;
if (glm::length2(_serverVelocity) > 0.0f) { if (glm::length2(_serverVelocity) > 0.0f) {
// the entity-server doesn't know where avatars are, so it doesn't do simple extrapolation for children of // the entity-server doesn't know where avatars are, so it doesn't do simple extrapolation for children of
@ -525,8 +530,6 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority); properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);
// copy _bidPriority into pendingPriority... // copy _bidPriority into pendingPriority...
_entity->setPendingOwnershipPriority(_bidPriority, now); _entity->setPendingOwnershipPriority(_bidPriority, now);
// don't forget to remember that we have made a bid
_entity->rememberHasSimulationOwnershipBid();
EntityTreeElementPointer element = _entity->getElement(); EntityTreeElementPointer element = _entity->getElement();
EntityTreePointer tree = element ? element->getTree() : nullptr; EntityTreePointer tree = element ? element->getTree() : nullptr;
@ -585,6 +588,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
// set the LastEdited of the properties but NOT the entity itself // set the LastEdited of the properties but NOT the entity itself
quint64 now = usecTimestampNow(); quint64 now = usecTimestampNow();
properties.setLastEdited(now); properties.setLastEdited(now);
_entity->setSimulationOwnershipExpiry(now + MAX_OUTGOING_SIMULATION_UPDATE_PERIOD);
if (_numInactiveUpdates > 0) { if (_numInactiveUpdates > 0) {
// the entity is stopped and inactive so we tell the server we're clearing simulatorID // the entity is stopped and inactive so we tell the server we're clearing simulatorID