mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-24 20:44:26 +02:00
Merge pull request #7196 from AndrewMeadows/simulation-ownership
no expiry for simulation ownership
This commit is contained in:
commit
2ffaa1f2e7
5 changed files with 107 additions and 65 deletions
|
@ -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.
|
// 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
|
// Since this version uses a single shared QMap for all senders, there could be some lock contention
|
||||||
// on this QWriteLocker
|
// 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);
|
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);
|
QWriteLocker locker(&_viewerSendingStatsLock);
|
||||||
_viewerSendingStats.remove(viewerNode);
|
_viewerSendingStats.remove(sessionID);
|
||||||
|
if (_entitySimulation) {
|
||||||
|
_entitySimulation->clearOwnership(sessionID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EntityServer::serverSubclassStats() {
|
QString EntityServer::serverSubclassStats() {
|
||||||
|
|
|
@ -27,6 +27,8 @@ struct ViewerSendingStats {
|
||||||
quint64 lastEdited;
|
quint64 lastEdited;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SimpleEntitySimulation;
|
||||||
|
|
||||||
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
|
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -52,8 +54,8 @@ public:
|
||||||
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override;
|
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override;
|
||||||
virtual QString serverSubclassStats() override;
|
virtual QString serverSubclassStats() override;
|
||||||
|
|
||||||
virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) override;
|
virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& sessionID) override;
|
||||||
virtual void trackViewerGone(const QUuid& viewerNode) override;
|
virtual void trackViewerGone(const QUuid& sessionID) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void pruneDeletedEntities();
|
void pruneDeletedEntities();
|
||||||
|
@ -65,7 +67,7 @@ private slots:
|
||||||
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EntitySimulation* _entitySimulation;
|
SimpleEntitySimulation* _entitySimulation;
|
||||||
QTimer* _pruneDeletedEntitiesTimer = nullptr;
|
QTimer* _pruneDeletedEntitiesTimer = nullptr;
|
||||||
|
|
||||||
QReadWriteLock _viewerSendingStatsLock;
|
QReadWriteLock _viewerSendingStatsLock;
|
||||||
|
|
|
@ -18,51 +18,69 @@
|
||||||
#include "EntityItem.h"
|
#include "EntityItem.h"
|
||||||
#include "EntitiesLogging.h"
|
#include "EntitiesLogging.h"
|
||||||
|
|
||||||
const quint64 MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD = 2 * USECS_PER_SECOND;
|
const quint64 MAX_OWNERLESS_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;
|
|
||||||
|
|
||||||
|
void SimpleEntitySimulation::clearOwnership(const QUuid& ownerID) {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin();
|
SetOfEntities::iterator itemItr = _entitiesWithSimulationOwner.begin();
|
||||||
while (itemItr != _entitiesWithSimulator.end()) {
|
while (itemItr != _entitiesWithSimulationOwner.end()) {
|
||||||
EntityItemPointer entity = *itemItr;
|
EntityItemPointer entity = *itemItr;
|
||||||
quint64 expiry = entity->getLastChangedOnServer() + MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD;
|
if (entity->getSimulatorID() == ownerID) {
|
||||||
if (expiry < now) {
|
// the simulator has abandonded this object --> remove from owned list
|
||||||
if (entity->getSimulatorID().isNull()) {
|
qCDebug(entities) << "auto-removing simulation owner " << entity->getSimulatorID();
|
||||||
// no simulators are volunteering
|
itemItr = _entitiesWithSimulationOwner.erase(itemItr);
|
||||||
// zero the velocity on this entity so that it doesn't drift far away
|
|
||||||
entity->setVelocity(Vectors::ZERO);
|
if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
||||||
entity->setAngularVelocity(Vectors::ZERO);
|
// it is still moving dynamically --> add to orphaned list
|
||||||
entity->setAcceleration(Vectors::ZERO);
|
_entitiesThatNeedSimulationOwner.insert(entity);
|
||||||
// remove from list
|
quint64 expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
||||||
itemItr = _entitiesWithSimulator.erase(itemItr);
|
if (expiry < _nextOwnerlessExpiry) {
|
||||||
continue;
|
_nextOwnerlessExpiry = expiry;
|
||||||
} 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove ownership and dirty all the tree elements that contain the it
|
||||||
|
entity->clearSimulationOwnership();
|
||||||
entity->markAsChangedOnServer();
|
entity->markAsChangedOnServer();
|
||||||
// dirty all the tree elements that contain the entity
|
|
||||||
DirtyOctreeElementOperator op(entity->getElement());
|
DirtyOctreeElementOperator op(entity->getElement());
|
||||||
getEntityTree()->recurseTreeWithOperator(&op);
|
getEntityTree()->recurseTreeWithOperator(&op);
|
||||||
} else if (expiry < _nextSimulationExpiry) {
|
} else {
|
||||||
_nextSimulationExpiry = expiry;
|
++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,47 @@ void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
EntitySimulation::addEntityInternal(entity);
|
EntitySimulation::addEntityInternal(entity);
|
||||||
if (!entity->getSimulatorID().isNull()) {
|
if (!entity->getSimulatorID().isNull()) {
|
||||||
QMutexLocker lock(&_mutex);
|
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) {
|
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||||
EntitySimulation::removeEntityInternal(entity);
|
EntitySimulation::removeEntityInternal(entity);
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
_entitiesWithSimulator.remove(entity);
|
_entitiesWithSimulationOwner.remove(entity);
|
||||||
|
_entitiesThatNeedSimulationOwner.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
EntitySimulation::changeEntityInternal(entity);
|
EntitySimulation::changeEntityInternal(entity);
|
||||||
if (!entity->getSimulatorID().isNull()) {
|
if (entity->getSimulatorID().isNull()) {
|
||||||
QMutexLocker lock(&_mutex);
|
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();
|
entity->clearDirtyFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::clearEntitiesInternal() {
|
void SimpleEntitySimulation::clearEntitiesInternal() {
|
||||||
_entitiesWithSimulator.clear();
|
QMutexLocker lock(&_mutex);
|
||||||
|
_entitiesWithSimulationOwner.clear();
|
||||||
|
_entitiesThatNeedSimulationOwner.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ public:
|
||||||
SimpleEntitySimulation() : EntitySimulation() { }
|
SimpleEntitySimulation() : EntitySimulation() { }
|
||||||
virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); }
|
virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); }
|
||||||
|
|
||||||
|
void clearOwnership(const QUuid& ownerID);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void updateEntitiesInternal(const quint64& now) override;
|
virtual void updateEntitiesInternal(const quint64& now) override;
|
||||||
virtual void addEntityInternal(EntityItemPointer entity) override;
|
virtual void addEntityInternal(EntityItemPointer entity) override;
|
||||||
|
@ -28,8 +30,9 @@ protected:
|
||||||
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
||||||
virtual void clearEntitiesInternal() override;
|
virtual void clearEntitiesInternal() override;
|
||||||
|
|
||||||
SetOfEntities _entitiesWithSimulator;
|
SetOfEntities _entitiesWithSimulationOwner;
|
||||||
quint64 _nextSimulationExpiry { 0 };
|
SetOfEntities _entitiesThatNeedSimulationOwner;
|
||||||
|
quint64 _nextOwnerlessExpiry { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_SimpleEntitySimulation_h
|
#endif // hifi_SimpleEntitySimulation_h
|
||||||
|
|
|
@ -272,7 +272,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
|
|
||||||
if (_numInactiveUpdates > 0) {
|
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) {
|
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
|
||||||
// clear local ownership (stop sending updates) and let the server clear itself
|
// clear local ownership (stop sending updates) and let the server clear itself
|
||||||
_entity->clearSimulationOwnership();
|
_entity->clearSimulationOwnership();
|
||||||
|
@ -282,7 +282,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
// until it is removed from the outgoing updates
|
// until it is removed from the outgoing updates
|
||||||
// (which happens when we don't own the simulation and it isn't touching our simulation)
|
// (which happens when we don't own the simulation and it isn't touching our simulation)
|
||||||
const float INACTIVE_UPDATE_PERIOD = 0.5f;
|
const float INACTIVE_UPDATE_PERIOD = 0.5f;
|
||||||
return (dt > INACTIVE_UPDATE_PERIOD);
|
return (dt > INACTIVE_UPDATE_PERIOD * (float)_numInactiveUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_body->isActive()) {
|
if (!_body->isActive()) {
|
||||||
|
@ -404,8 +404,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
||||||
assert(_entity);
|
assert(_entity);
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
|
|
||||||
bool active = _body->isActive();
|
if (!_body->isActive()) {
|
||||||
if (!active) {
|
|
||||||
// make sure all derivatives are zero
|
// make sure all derivatives are zero
|
||||||
glm::vec3 zero(0.0f);
|
glm::vec3 zero(0.0f);
|
||||||
_entity->setVelocity(zero);
|
_entity->setVelocity(zero);
|
||||||
|
@ -495,16 +494,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
||||||
qCDebug(physics) << " lastSimulated:" << debugTime(lastSimulated, now);
|
qCDebug(physics) << " lastSimulated:" << debugTime(lastSimulated, now);
|
||||||
#endif //def WANT_DEBUG
|
#endif //def WANT_DEBUG
|
||||||
|
|
||||||
if (sessionID == _entity->getSimulatorID()) {
|
if (_numInactiveUpdates > 0) {
|
||||||
// we think we own the simulation
|
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID
|
||||||
if (!active) {
|
// but we remember that we do still own it... and rely on the server to tell us that we don't
|
||||||
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID
|
properties.clearSimulationOwner();
|
||||||
// but we remember that we do still own it... and rely on the server to tell us that we don't
|
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||||
properties.clearSimulationOwner();
|
} else if (sessionID != _entity->getSimulatorID()) {
|
||||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
|
||||||
}
|
|
||||||
// else the ownership is not changing so we don't bother to pack it
|
|
||||||
} else {
|
|
||||||
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
// 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));
|
properties.setSimulationOwner(sessionID, glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY));
|
||||||
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||||
|
|
Loading…
Reference in a new issue