mirror of
https://github.com/lubosz/overte.git
synced 2025-04-08 07:22:43 +02:00
add simulation ownership expiry
This commit is contained in:
parent
0c9d65bc15
commit
a0f3e3a031
11 changed files with 105 additions and 68 deletions
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
DirtyOctreeElementOperator::DirtyOctreeElementOperator(const OctreeElementPointer& element)
|
||||
: _element(element) {
|
||||
assert(_element.get());
|
||||
_element->bumpChangedContent();
|
||||
_point = _element->getAACube().calcCenter();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue