more work on simulator priority

scripts that edit terse update data try to assert priority
physics simulation tries to assert "volunteer" priority
max priority rules are applied in entity server
This commit is contained in:
Andrew Meadows 2015-06-12 13:39:40 -07:00
parent 82ba5cd4b6
commit d0ac3e4514
10 changed files with 104 additions and 46 deletions

View file

@ -27,7 +27,8 @@
#include "EntityTree.h"
#include "EntitySimulation.h"
const quint64 SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND);
const quint64 DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND);
const quint64 MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD = 2 * USECS_PER_SECOND;
bool EntityItem::_sendPhysicsUpdates = true;
@ -322,6 +323,7 @@ int EntityItem::expectedBytes() {
}
// clients use this method to unpack FULL updates from entity-server
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) {
@ -596,24 +598,22 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// ownership has changed
auto nodeList = DependencyManager::get<NodeList>();
if (_simulatorID == nodeList->getSessionUUID()) {
// we think we're the owner but entityServer says otherwise
// we relenquish ownership if the incoming priority is greater than or equal to ours
// we think we're the simulation owner but entity-server says otherwise
// we relenquish ownership iff the incoming priority is greater than or equal to ours
// AND we don't have max priority
if (priority >= _simulatorPriority && _simulatorPriority != MAX_SIMULATOR_PRIORITY) {
// we're losing simulation ownership
_simulatorID = id;
_simulatorPriority = priority;
_simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD;
}
} else {
_simulatorID = id;
_simulatorPriority = priority;
_simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD;
}
} else if (priority != _simulatorPriority) {
// priority is changing but simulatorID is not.
// only accept this change if we are NOT the simulator owner, since otherwise
// we would have initiated this change
// we would have initiated this priority
auto nodeList = DependencyManager::get<NodeList>();
if (_simulatorID != nodeList->getSessionUUID()) {
_simulatorPriority = priority;
@ -1395,21 +1395,51 @@ void EntityItem::updateCreated(uint64_t value) {
}
}
void EntityItem::setSimulatorPriority(uint8_t priority) {
_simulatorPriority = priority;
if (_simulatorPriority == MAX_SIMULATOR_PRIORITY) {
// we always extend the the ownership expiry for MAX_SIMULATOR_PRIORITY
_simulationOwnershipExpiry = usecTimestampNow() + MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD;
} else if (_simulatorPriority == 0) {
_simulationOwnershipExpiry = 0;
}
}
void EntityItem::setSimulatorID(const QUuid& value) {
if (_simulatorID != value) {
_simulatorID = value;
_simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD;
if (!_simulatorID.isNull()) {
// Note: this logic only works well if _simulatorPriority is properly set before this point
quint64 lockoutPeriod = (_simulatorPriority == MAX_SIMULATOR_PRIORITY)
? MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD : DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD;
_simulationOwnershipExpiry = usecTimestampNow() + lockoutPeriod;
}
}
}
void EntityItem::updateSimulatorID(const QUuid& value) {
if (_simulatorID != value) {
_simulatorID = value;
_simulationOwnershipExpiry = usecTimestampNow() + SIMULATOR_CHANGE_LOCKOUT_PERIOD;
if (!_simulatorID.isNull()) {
// Note: this logic only works well if _simulatorPriority is properly set before this point
quint64 lockoutPeriod = (_simulatorPriority == MAX_SIMULATOR_PRIORITY)
? MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD : DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD;
_simulationOwnershipExpiry = usecTimestampNow() + lockoutPeriod;
}
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
}
}
void EntityItem::clearSimulationOwnership() {
_simulatorPriority = 0;
_simulatorID = QUuid();
_simulationOwnershipExpiry = 0;
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulatorOwnership()
// is only ever called entity-server-side and the flags are only used client-side
//_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
}
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
assert(action);
const QUuid& actionID = action->getID();

View file

@ -60,8 +60,10 @@ const float ACTIVATION_LINEAR_VELOCITY_DELTA = 0.01f;
const float ACTIVATION_GRAVITY_DELTA = 0.1f;
const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
const uint8_t MIN_SIMULATOR_PRIORITY = 1;
const uint8_t VOLUNTEER_SIMULATOR_PRIORITY = 0x01;
const uint8_t SCRIPT_EDIT_SIMULATOR_PRIORITY = 0x80;
const uint8_t MAX_SIMULATOR_PRIORITY = 0xff;
const uint8_t ATTACHMENT_SIMULATOR_PRIORITY = MAX_SIMULATOR_PRIORITY;
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };
@ -320,13 +322,14 @@ public:
const QString& getUserData() const { return _userData; }
void setUserData(const QString& value) { _userData = value; }
void setSimulatorPriority(uint8_t priority) { _simulatorPriority = priority; }
void setSimulatorPriority(uint8_t priority);
uint8_t getSimulatorPriority() const { return _simulatorPriority; }
QUuid getSimulatorID() const { return _simulatorID; }
void setSimulatorID(const QUuid& value);
void updateSimulatorID(const QUuid& value);
const quint64& getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; }
void clearSimulationOwnership();
const QString& getMarketplaceID() const { return _marketplaceID; }
void setMarketplaceID(const QString& value) { _marketplaceID = value; }

View file

@ -1234,3 +1234,17 @@ bool EntityItemProperties::hasTerseUpdateChanges() const {
// a TerseUpdate includes the transform and its derivatives
return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged;
}
void EntityItemProperties::clearSimulatorOwnership() {
_simulatorID = QUuid();
_simulatorPriority = 0;
_simulatorIDChanged = true;
_simulatorPriorityChanged = true;
}
void EntityItemProperties::setSimulatorOwnership(const QUuid& id, uint8_t priority) {
_simulatorID = id;
_simulatorPriority = glm::max(priority, _simulatorPriority);
_simulatorIDChanged = true;
_simulatorPriorityChanged = true;
}

View file

@ -207,6 +207,9 @@ public:
bool hasTerseUpdateChanges() const;
void clearSimulatorOwnership();
void setSimulatorOwnership(const QUuid& id, uint8_t priority);
private:
QUuid _id;
bool _idSet;

View file

@ -61,16 +61,6 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
}
}
void bidForSimulationOwnership(EntityItemProperties& properties) {
// We make a bid for simulation ownership by declaring our sessionID as simulation owner
// in the outgoing properties. The EntityServer may accept the bid or might not.
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
properties.setSimulatorID(myNodeID);
}
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
EntityItemProperties propertiesWithSimID = properties;
@ -83,11 +73,15 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
_entityTree->lockForWrite();
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
if (entity) {
entity->setLastBroadcast(usecTimestampNow());
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
bidForSimulationOwnership(propertiesWithSimID);
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
propertiesWithSimID.setSimulatorOwnership(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY);
// and make note of it now, so we can act on it right away.
entity->setSimulatorID(propertiesWithSimID.getSimulatorID());
entity->setLastBroadcast(usecTimestampNow());
} else {
qCDebug(entities) << "script failed to add new Entity to local Octree";
success = false;
@ -160,9 +154,14 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties proper
// balls" test. However, even if we solve this problem we still need to provide a "slerp the visible
// proxy toward the true physical position" feature to hide the final glitches in the remote watcher's
// simulation.
// we re-assert our simulation ownership
properties.setSimulatorOwnership(myNodeID,
glm::max(entity->getSimulatorPriority(), SCRIPT_EDIT_SIMULATOR_PRIORITY));
} else {
// we make a bid for simulation ownership
properties.setSimulatorOwnership(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY);
}
// we make a bid for (or assert existing) simulation ownership
properties.setSimulatorID(myNodeID);
}
entity->setLastBroadcast(usecTimestampNow());
}

View file

@ -169,15 +169,12 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
// so we apply the rules for ownership change:
// (1) higher priority wins
// (2) equal priority wins if ownership filter has expired except...
// (3) max priority never loses
uint8_t oldPriority = entity->getSimulatorPriority();
if (oldPriority != MAX_SIMULATOR_PRIORITY) {
uint8_t newPriority = properties.getSimulatorPriority();
if (newPriority > oldPriority ||
(newPriority == oldPriority &&
usecTimestampNow() > entity->getSimulationOwnershipExpiry())) {
simulationBlocked = false;
}
uint8_t newPriority = properties.getSimulatorPriority();
if (newPriority > oldPriority ||
(newPriority == oldPriority &&
usecTimestampNow() > entity->getSimulationOwnershipExpiry())) {
simulationBlocked = false;
}
}
} else {

View file

@ -23,18 +23,18 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
// has finished simulating it.
auto nodeList = DependencyManager::get<LimitedNodeList>();
SetOfEntities::iterator itemItr = _hasSimulationOwnerEntities.begin();
while (itemItr != _hasSimulationOwnerEntities.end()) {
SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin();
while (itemItr != _entitiesWithSimulator.end()) {
EntityItemPointer entity = *itemItr;
if (entity->getSimulatorID().isNull()) {
itemItr = _hasSimulationOwnerEntities.erase(itemItr);
itemItr = _entitiesWithSimulator.erase(itemItr);
} else if (now - entity->getLastChangedOnServer() >= AUTO_REMOVE_SIMULATION_OWNER_USEC) {
SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID());
if (ownerNode.isNull() || !ownerNode->isAlive()) {
qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID();
// TODO: zero velocities when we clear simulatorID?
entity->setSimulatorID(QUuid());
itemItr = _hasSimulationOwnerEntities.erase(itemItr);
entity->clearSimulationOwnership();
itemItr = _entitiesWithSimulator.erase(itemItr);
} else {
++itemItr;
}
@ -47,23 +47,23 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
EntitySimulation::addEntityInternal(entity);
if (!entity->getSimulatorID().isNull()) {
_hasSimulationOwnerEntities.insert(entity);
_entitiesWithSimulator.insert(entity);
}
}
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
_hasSimulationOwnerEntities.remove(entity);
_entitiesWithSimulator.remove(entity);
}
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
EntitySimulation::changeEntityInternal(entity);
if (!entity->getSimulatorID().isNull()) {
_hasSimulationOwnerEntities.insert(entity);
_entitiesWithSimulator.insert(entity);
}
entity->clearDirtyFlags();
}
void SimpleEntitySimulation::clearEntitiesInternal() {
_hasSimulationOwnerEntities.clear();
_entitiesWithSimulator.clear();
}

View file

@ -28,7 +28,7 @@ protected:
virtual void changeEntityInternal(EntityItemPointer entity);
virtual void clearEntitiesInternal();
SetOfEntities _hasSimulationOwnerEntities;
SetOfEntities _entitiesWithSimulator;
};
#endif // hifi_SimpleEntitySimulation_h

View file

@ -97,7 +97,9 @@ void EntityMotionState::handleEasyChanges(uint32_t flags) {
assert(entityTreeIsLocked());
updateServerPhysicsVariables();
ObjectMotionState::handleEasyChanges(flags);
if (flags & EntityItem::DIRTY_SIMULATOR_ID) {
_loopsSinceOwnershipBid = 0;
_loopsWithoutOwner = 0;
_candidateForOwnership = 0;
if (_entity->getSimulatorID().isNull()
@ -453,14 +455,15 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
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.setSimulatorID(QUuid());
properties.clearSimulatorOwnership();
} else {
// explicitly set the property's simulatorID so that it is flagged as changed and will be packed
properties.setSimulatorID(sessionID);
// re-assert the simulation info
properties.setSimulatorOwnership(sessionID, _entity->getSimulatorPriority());
}
} else {
// we don't own the simulation for this entity yet, but we're sending a bid for it
properties.setSimulatorID(sessionID);
properties.setSimulatorOwnership(sessionID, glm::max(_simulatorPriorityHint, VOLUNTEER_SIMULATOR_PRIORITY));
_simulatorPriorityHint = 0;
}
if (EntityItem::getSendPhysicsUpdates()) {
@ -580,3 +583,8 @@ int16_t EntityMotionState::computeCollisionGroup() {
}
return COLLISION_GROUP_DEFAULT;
}
void EntityMotionState::setSimulatorPriorityHint(uint8_t priority) {
_candidateForOwnership = true;
_simulatorPriorityHint = priority;
}

View file

@ -80,6 +80,9 @@ public:
virtual int16_t computeCollisionGroup();
// eternal logic can suggest a simuator priority bid for the next outgoing update
void setSimulatorPriorityHint(uint8_t priority);
friend class PhysicalEntitySimulation;
protected:
@ -114,6 +117,7 @@ protected:
bool _candidateForOwnership;
uint32_t _loopsSinceOwnershipBid;
uint32_t _loopsWithoutOwner;
uint8_t _simulatorPriorityHint;
};
#endif // hifi_EntityMotionState_h