add priority promotion to reduce volunteer races

also fix priority inheritance from chained collisions
This commit is contained in:
Andrew Meadows 2015-06-26 22:30:06 -07:00
parent f274958beb
commit 4d4b97fe59
9 changed files with 26 additions and 46 deletions

View file

@ -349,12 +349,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
return 0; return 0;
} }
// if this bitstream indicates that this node is the simulation owner, ignore any physics-related updates.
glm::vec3 savePosition = getPosition();
glm::quat saveRotation = getRotation();
glm::vec3 saveVelocity = _velocity;
glm::vec3 saveAngularVelocity = _angularVelocity;
int originalLength = bytesLeftToRead; int originalLength = bytesLeftToRead;
// TODO: figure out a way to avoid the big deep copy below. // TODO: figure out a way to avoid the big deep copy below.
QByteArray originalDataBuffer((const char*)data, originalLength); // big deep copy! QByteArray originalDataBuffer((const char*)data, originalLength); // big deep copy!
@ -550,17 +544,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
dataAt += bytes; dataAt += bytes;
bytesRead += bytes; bytesRead += bytes;
if (overwriteLocalData) { if (_simulationOwner.set(newSimOwner)) {
if (_simulationOwner.set(newSimOwner)) { _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
}
} }
} }
{ { // When we own the simulation we don't accept updates to the entity's transform/velocities
// but since we're using macros below we have to temporarily modify overwriteLocalData.
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
bool weOwnIt = _simulationOwner.matchesValidID(nodeList->getSessionUUID()); bool weOwnIt = _simulationOwner.matchesValidID(nodeList->getSessionUUID());
// When we own the simulation we don't accept updates to the entity's transform/velocities
// but since we're using macros below we have to temporarily modify overwriteLocalData.
bool oldOverwrite = overwriteLocalData; bool oldOverwrite = overwriteLocalData;
overwriteLocalData = overwriteLocalData && !weOwnIt; overwriteLocalData = overwriteLocalData && !weOwnIt;
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
@ -665,15 +656,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
const QUuid& myNodeID = nodeList->getSessionUUID(); const QUuid& myNodeID = nodeList->getSessionUUID();
if (overwriteLocalData) { if (overwriteLocalData) {
if (_simulationOwner.matchesValidID(myNodeID)) { if (!_simulationOwner.matchesValidID(myNodeID)) {
// we own the simulation, so we keep our transform+velocities and remove any related dirty flags
// rather than accept the values in the packet
setPosition(savePosition);
setRotation(saveRotation);
_velocity = saveVelocity;
_angularVelocity = saveAngularVelocity;
_dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES);
} else {
_lastSimulated = now; _lastSimulated = now;
} }
} }
@ -988,6 +972,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = false; bool somethingChanged = false;
// these affect TerseUpdate properties // these affect TerseUpdate properties
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition); SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity);
@ -1009,7 +994,6 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated); SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner);
// non-simulation properties below // non-simulation properties below
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
@ -1392,10 +1376,6 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
_simulationOwner.set(owner); _simulationOwner.set(owner);
} }
void EntityItem::promoteSimulationPriority(quint8 priority) {
_simulationOwner.promotePriority(priority);
}
void EntityItem::updateSimulatorID(const QUuid& value) { void EntityItem::updateSimulatorID(const QUuid& value) {
if (_simulationOwner.setID(value)) { if (_simulationOwner.setID(value)) {
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
@ -1404,7 +1384,7 @@ void EntityItem::updateSimulatorID(const QUuid& value) {
void EntityItem::clearSimulationOwnership() { void EntityItem::clearSimulationOwnership() {
_simulationOwner.clear(); _simulationOwner.clear();
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulatorOwnership() // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership()
// is only ever called entity-server-side and the flags are only used client-side // is only ever called entity-server-side and the flags are only used client-side
//_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; //_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;

View file

@ -706,7 +706,6 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
// PROP_PAGED_PROPERTY, // PROP_PAGED_PROPERTY,
// PROP_CUSTOM_PROPERTIES_INCLUDED, // PROP_CUSTOM_PROPERTIES_INCLUDED,
// adebug TODO: convert this to use APPEND_ENTITY_PROPERTY(P,V) macro?
if (requestedProperties.getHasProperty(PROP_SIMULATION_OWNER)) { if (requestedProperties.getHasProperty(PROP_SIMULATION_OWNER)) {
LevelDetails propertyLevel = packetData->startLevel(); LevelDetails propertyLevel = packetData->startLevel();
successPropertyFits = packetData->appendValue(properties._simulationOwner.toByteArray()); successPropertyFits = packetData->appendValue(properties._simulationOwner.toByteArray());
@ -977,7 +976,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
dataAt += propertyFlags.getEncodedLength(); dataAt += propertyFlags.getEncodedLength();
processedBytes += propertyFlags.getEncodedLength(); processedBytes += propertyFlags.getEncodedLength();
// adebug TODO: convert this to use READ_ENTITY_PROPERTY_TO_PROPERTIES macro?
if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
QByteArray fromBuffer; QByteArray fromBuffer;
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer); int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer);

View file

@ -210,6 +210,7 @@ public:
void clearSimulationOwner(); void clearSimulationOwner();
void setSimulationOwner(const QUuid& id, uint8_t priority); void setSimulationOwner(const QUuid& id, uint8_t priority);
void promoteSimulationPriority(quint8 priority) { _simulationOwner.promotePriority(priority); }
private: private:
QUuid _id; QUuid _id;

View file

@ -121,10 +121,10 @@ enum EntityPropertyList {
// used by hyperlinks // used by hyperlinks
PROP_HREF, PROP_HREF,
PROP_DESCRIPTION, PROP_DESCRIPTION,
PROP_SIMULATION_OWNER,
PROP_FACE_CAMERA, PROP_FACE_CAMERA,
PROP_SCRIPT_TIMESTAMP, PROP_SCRIPT_TIMESTAMP,
PROP_SIMULATION_OWNER,
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties ABOVE this line // ATTENTION: add new properties ABOVE this line

View file

@ -161,9 +161,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties proper
// proxy toward the true physical position" feature to hide the final glitches in the remote watcher's // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's
// simulation. // simulation.
// we re-assert our simulation ownership if (entity->getSimulationPriority() < SCRIPT_EDIT_SIMULATION_PRIORITY) {
properties.setSimulationOwner(myNodeID, // we re-assert our simulation ownership at a higher priority
glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY)); properties.setSimulationOwner(myNodeID,
glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY));
}
} else { } else {
// we make a bid for simulation ownership // we make a bid for simulation ownership
properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY); properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);

View file

@ -162,7 +162,12 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
// else: We assume the sender really did believe it was the simulation owner when it sent // else: We assume the sender really did believe it was the simulation owner when it sent
} else if (submittedID == senderID) { } else if (submittedID == senderID) {
// the sender is trying to take or continue ownership // the sender is trying to take or continue ownership
if (entity->getSimulatorID().isNull() || entity->getSimulatorID() == senderID) { if (entity->getSimulatorID().isNull()) {
// the sender it taking ownership
properties.promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
simulationBlocked = false;
} else if (entity->getSimulatorID() == senderID) {
// the sender is asserting ownership
simulationBlocked = false; simulationBlocked = false;
} else { } else {
// the sender is trying to steal ownership from another simulator // the sender is trying to steal ownership from another simulator
@ -180,6 +185,8 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
// the entire update is suspect --> ignore it // the entire update is suspect --> ignore it
return false; return false;
} }
} else {
simulationBlocked = senderID != entity->getSimulatorID();
} }
if (simulationBlocked) { if (simulationBlocked) {
// squash ownership and physics-related changes. // squash ownership and physics-related changes.
@ -208,10 +215,6 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
_simulation->lock(); _simulation->lock();
_simulation->changeEntity(entity); _simulation->changeEntity(entity);
_simulation->unlock(); _simulation->unlock();
// always promote volunteer priority
if (entity->getSimulationPriority() == VOLUNTEER_SIMULATION_PRIORITY) {
entity->promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
}
} }
} else { } else {
// normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly // normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly

View file

@ -67,9 +67,7 @@ bool SimulationOwner::setID(const QUuid& id) {
_id = id; _id = id;
updateExpiry(); updateExpiry();
if (_id.isNull()) { if (_id.isNull()) {
// when id is null we clear everything
_priority = 0; _priority = 0;
_expiry = 0;
} }
return true; return true;
} }

View file

@ -249,8 +249,6 @@ public:
static int unpackDataFromBytes(const unsigned char* dataBytes, QVector<glm::vec3>& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector<glm::vec3>& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result);
QByteArray getRawData() const { return QByteArray((const char*)_uncompressed, _bytesInUse); } // adebug
private: private:
/// appends raw bytes, might fail if byte would cause packet to be too large /// appends raw bytes, might fail if byte would cause packet to be too large
bool append(const unsigned char* data, int length); bool append(const unsigned char* data, int length);

View file

@ -118,7 +118,7 @@ void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
} }
} }
if (flags & EntityItem::DIRTY_SIMULATOR_OWNERSHIP) { if (flags & EntityItem::DIRTY_SIMULATOR_OWNERSHIP) {
// also known as "bid for ownership with SCRIPT priority" // (DIRTY_SIMULATOR_OWNERSHIP really means "we should bid for ownership with SCRIPT priority")
// we're manipulating this object directly via script, so we artificially // we're manipulating this object directly via script, so we artificially
// manipulate the logic to trigger an immediate bid for ownership // manipulate the logic to trigger an immediate bid for ownership
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
@ -449,6 +449,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID // 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 // but we remember that we do still own it... and rely on the server to tell us that we don't
properties.clearSimulationOwner(); properties.clearSimulationOwner();
_outgoingPriority = 0;
} }
// else the ownership is not changing so we don't bother to pack it // else the ownership is not changing so we don't bother to pack it
} else { } else {
@ -512,8 +513,7 @@ QUuid EntityMotionState::getSimulatorID() const {
// virtual // virtual
void EntityMotionState::bump(quint8 priority) { void EntityMotionState::bump(quint8 priority) {
if (_entity) { if (_entity) {
quint8 inheritedPriority = VOLUNTEER_SIMULATION_PRIORITY; setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
setOutgoingPriority(inheritedPriority);
} }
} }