no expiry for simulation ownership

This commit is contained in:
Andrew Meadows 2016-02-25 12:06:22 -08:00
parent 91f6b7e80d
commit b4edfe2390
5 changed files with 106 additions and 65 deletions

View file

@ -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() {

View file

@ -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<ReceivedMessage> message, SharedNodePointer senderNode);
private:
EntitySimulation* _entitySimulation;
SimpleEntitySimulation* _entitySimulation;
QTimer* _pruneDeletedEntitiesTimer = nullptr;
QReadWriteLock _viewerSendingStatsLock;

View file

@ -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();
}

View file

@ -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

View file

@ -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<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY));
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;