add simulation ownership expiry

This commit is contained in:
Andrew Meadows 2018-04-05 12:51:59 -07:00
parent 0c9d65bc15
commit a0f3e3a031
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));
_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
// TODO: sort these correctly
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
@ -330,7 +331,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
return;
}
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));
_entitiesInQueue.insert(entity.get());
}
@ -382,7 +385,8 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
_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
// TODO: sort these correctly
_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
_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,
// this could happen when the user reloads the cache and entity tree.
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
@ -1946,10 +1946,6 @@ void EntityItem::setPendingOwnershipPriority(uint8_t priority, const quint64& ti
_simulationOwner.setPendingPriority(priority, timestamp);
}
void EntityItem::rememberHasSimulationOwnershipBid() const {
_hasBidOnSimulation = true;
}
QString EntityItem::actionsToDebugString() {
QString result;
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 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;
/// 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);
std::unordered_map<std::string, graphics::MultiMaterial> getMaterials();
void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; }
uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
signals:
void requestRenderUpdate();
@ -619,9 +625,6 @@ protected:
static quint64 _rememberDeletedActionTime;
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
bool _clientOnly { false };
@ -642,6 +645,7 @@ protected:
quint64 _lastUpdatedAngularVelocityTimestamp { 0 };
quint64 _lastUpdatedAccelerationTimestamp { 0 };
quint64 _lastUpdatedQueryAACubeTimestamp { 0 };
uint64_t _simulationOwnershipExpiry { 0 };
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
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
entity->rememberHasSimulationOwnershipBid();
}
}
if (properties.queryAACubeRelatedPropertyChanged()) {

View file

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

View file

@ -243,14 +243,10 @@ public:
return std::static_pointer_cast<const OctreeElement>(shared_from_this());
}
void bumpChangedContent() { _lastChangedContent = usecTimestampNow(); }
uint64_t getLastChangedContent() const { return _lastChangedContent; }
protected:
virtual void init(unsigned char * octalCode) override;
EntityTreePointer _myTree;
EntityItems _entityItems;
uint64_t _lastChangedContent { 0 };
};
#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
//
//#include <PerfStat.h>
#include "SimpleEntitySimulation.h"
#include <DirtyOctreeElementOperator.h>
@ -27,16 +25,13 @@ void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
EntityItemPointer entity = *itemItr;
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);
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
if (expiry < _nextOwnerlessExpiry) {
_nextOwnerlessExpiry = expiry;
}
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
}
// 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) {
if (now > _nextOwnerlessExpiry) {
// search for ownerless objects that have expired
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;
}
}
}
}
expireStaleOwnerships(now);
stopOwnerlessEntities(now);
}
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
@ -93,13 +59,12 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
if (!entity->getSimulatorID().isNull()) {
QMutexLocker lock(&_mutex);
_entitiesWithSimulationOwner.insert(entity);
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
} else if (entity->getDynamic() && entity->hasLocalVelocity()) {
QMutexLocker lock(&_mutex);
_entitiesThatNeedSimulationOwner.insert(entity);
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
if (expiry < _nextOwnerlessExpiry) {
_nextOwnerlessExpiry = expiry;
}
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
}
}
@ -128,13 +93,12 @@ void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
if (entity->getDynamic() && entity->hasLocalVelocity()) {
_entitiesThatNeedSimulationOwner.insert(entity);
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
if (expiry < _nextOwnerlessExpiry) {
_nextOwnerlessExpiry = expiry;
}
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
}
} else {
QMutexLocker lock(&_mutex);
_entitiesWithSimulationOwner.insert(entity);
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
_entitiesThatNeedSimulationOwner.remove(entity);
}
entity->clearDirtyFlags();
@ -155,3 +119,58 @@ void SimpleEntitySimulation::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 {
public:
SimpleEntitySimulation() : EntitySimulation() { }
virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); }
~SimpleEntitySimulation() { clearEntitiesInternal(); }
void clearOwnership(const QUuid& ownerID);
protected:
virtual void updateEntitiesInternal(uint64_t now) override;
virtual void addEntityInternal(EntityItemPointer entity) override;
virtual void removeEntityInternal(EntityItemPointer entity) override;
virtual void changeEntityInternal(EntityItemPointer entity) override;
virtual void clearEntitiesInternal() override;
void updateEntitiesInternal(uint64_t now) override;
void addEntityInternal(EntityItemPointer entity) override;
void removeEntityInternal(EntityItemPointer entity) override;
void changeEntityInternal(EntityItemPointer entity) override;
void clearEntitiesInternal() override;
virtual void sortEntitiesThatMoved() override;
void sortEntitiesThatMoved() override;
void expireStaleOwnerships(uint64_t now);
void stopOwnerlessEntities(uint64_t now);
SetOfEntities _entitiesWithSimulationOwner;
SetOfEntities _entitiesThatNeedSimulationOwner;
uint64_t _nextOwnerlessExpiry { 0 };
uint64_t _nextStaleOwnershipExpiry { (uint64_t)(-1) };
};
#endif // hifi_SimpleEntitySimulation_h

View file

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

View file

@ -219,6 +219,9 @@ public:
int getMyChildContaining(const AABox& box) const;
int getMyChildContainingPoint(const glm::vec3& point) const;
void bumpChangedContent() { _lastChangedContent = usecTimestampNow(); }
uint64_t getLastChangedContent() const { return _lastChangedContent; }
protected:
void deleteAllChildren();
@ -235,6 +238,7 @@ protected:
} _octalCode;
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
#ifdef SIMPLE_CHILD_ARRAY

View file

@ -365,6 +365,11 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
return true;
}
if (usecTimestampNow() > _entity->getSimulationOwnershipExpiry()) {
// send update every so often else server will revoke our ownership
return true;
}
_lastStep = simulationStep;
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
@ -525,8 +530,6 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);
// copy _bidPriority into pendingPriority...
_entity->setPendingOwnershipPriority(_bidPriority, now);
// don't forget to remember that we have made a bid
_entity->rememberHasSimulationOwnershipBid();
EntityTreeElementPointer element = _entity->getElement();
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
quint64 now = usecTimestampNow();
properties.setLastEdited(now);
_entity->setSimulationOwnershipExpiry(now + MAX_OUTGOING_SIMULATION_UPDATE_PERIOD);
if (_numInactiveUpdates > 0) {
// the entity is stopped and inactive so we tell the server we're clearing simulatorID