mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-06 07:10:19 +02:00
bid for simulation ownership
This commit is contained in:
parent
45d4fa91ab
commit
4872a565c9
11 changed files with 185 additions and 116 deletions
|
@ -565,7 +565,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA, setUserData);
|
READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA, setUserData);
|
||||||
|
|
||||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||||
READ_ENTITY_PROPERTY_UUID(PROP_SIMULATOR_ID, setSimulatorID);
|
READ_ENTITY_PROPERTY_UUID(PROP_SIMULATOR_ID, updateSimulatorID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
||||||
|
@ -940,7 +940,7 @@ 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(locked, setLocked);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, setSimulatorID);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, updateSimulatorID);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||||
|
|
||||||
|
@ -1270,8 +1270,14 @@ void EntityItem::updateLifetime(float value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::setSimulatorID(const QUuid& value) {
|
void EntityItem::setSimulatorID(const QUuid& value) {
|
||||||
|
_simulatorID = value;
|
||||||
|
_simulatorIDChangedTime = usecTimestampNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateSimulatorID(const QUuid& value) {
|
||||||
if (_simulatorID != value) {
|
if (_simulatorID != value) {
|
||||||
_simulatorID = value;
|
_simulatorID = value;
|
||||||
_simulatorIDChangedTime = usecTimestampNow();
|
_simulatorIDChangedTime = usecTimestampNow();
|
||||||
|
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ public:
|
||||||
DIRTY_UPDATEABLE = 0x0200,
|
DIRTY_UPDATEABLE = 0x0200,
|
||||||
DIRTY_MATERIAL = 0x00400,
|
DIRTY_MATERIAL = 0x00400,
|
||||||
DIRTY_PHYSICS_ACTIVATION = 0x0800, // we want to activate the object
|
DIRTY_PHYSICS_ACTIVATION = 0x0800, // we want to activate the object
|
||||||
|
DIRTY_SIMULATOR_ID = 0x1000,
|
||||||
DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION,
|
DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION,
|
||||||
DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY
|
DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY
|
||||||
};
|
};
|
||||||
|
@ -284,6 +285,7 @@ public:
|
||||||
|
|
||||||
QUuid getSimulatorID() const { return _simulatorID; }
|
QUuid getSimulatorID() const { return _simulatorID; }
|
||||||
void setSimulatorID(const QUuid& value);
|
void setSimulatorID(const QUuid& value);
|
||||||
|
void updateSimulatorID(const QUuid& value);
|
||||||
quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; }
|
quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; }
|
||||||
|
|
||||||
const QString& getMarketplaceID() const { return _marketplaceID; }
|
const QString& getMarketplaceID() const { return _marketplaceID; }
|
||||||
|
|
|
@ -61,13 +61,12 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bidForSimulationOwnership(EntityItemProperties& properties) {
|
||||||
|
// We make a bid for simulation ownership by declaring our sessionID as simulation owner
|
||||||
void setSimId(EntityItemProperties& propertiesWithSimID, EntityItem* entity) {
|
// in the outgoing properties. The EntityServer may accept the bid or might not.
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||||
propertiesWithSimID.setSimulatorID(myNodeID);
|
properties.setSimulatorID(myNodeID);
|
||||||
entity->setSimulatorID(myNodeID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +88,7 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
if (entity) {
|
if (entity) {
|
||||||
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
|
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
|
||||||
setSimId(propertiesWithSimID, entity);
|
bidForSimulationOwnership(propertiesWithSimID);
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||||
success = false;
|
success = false;
|
||||||
|
@ -163,29 +162,31 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemProperties propertiesWithSimID = properties;
|
|
||||||
|
|
||||||
// If we have a local entity tree set, then also update it. We can do this even if we don't know
|
// If we have a local entity tree set, then also update it. We can do this even if we don't know
|
||||||
// the actual id, because we can edit out local entities just with creatorTokenID
|
// the actual id, because we can edit out local entities just with creatorTokenID
|
||||||
if (_entityTree) {
|
if (_entityTree) {
|
||||||
_entityTree->lockForWrite();
|
_entityTree->lockForWrite();
|
||||||
_entityTree->updateEntity(entityID, propertiesWithSimID, canAdjustLocks());
|
_entityTree->updateEntity(entityID, properties);
|
||||||
_entityTree->unlock();
|
_entityTree->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if at this point, we know the id, send the update to the entity server
|
// if at this point, we know the id, send the update to the entity server
|
||||||
if (entityID.isKnownID) {
|
if (entityID.isKnownID) {
|
||||||
// make sure the properties has a type, so that the encode can know which properties to include
|
// make sure the properties has a type, so that the encode can know which properties to include
|
||||||
if (propertiesWithSimID.getType() == EntityTypes::Unknown) {
|
if (properties.getType() == EntityTypes::Unknown) {
|
||||||
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
|
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
|
// we need to change the outgoing properties, so we make a copy, modify, and send.
|
||||||
|
EntityItemProperties modifiedProperties = properties;
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
propertiesWithSimID.setType(entity->getType());
|
modifiedProperties.setType(entity->getType());
|
||||||
setSimId(propertiesWithSimID, entity);
|
bidForSimulationOwnership(modifiedProperties);
|
||||||
|
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, modifiedProperties);
|
||||||
|
return entityID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, propertiesWithSimID);
|
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entityID;
|
return entityID;
|
||||||
|
|
|
@ -37,7 +37,8 @@ const int DIRTY_SIMULATION_FLAGS =
|
||||||
EntityItem::DIRTY_SHAPE |
|
EntityItem::DIRTY_SHAPE |
|
||||||
EntityItem::DIRTY_LIFETIME |
|
EntityItem::DIRTY_LIFETIME |
|
||||||
EntityItem::DIRTY_UPDATEABLE |
|
EntityItem::DIRTY_UPDATEABLE |
|
||||||
EntityItem::DIRTY_MATERIAL;
|
EntityItem::DIRTY_MATERIAL |
|
||||||
|
EntityItem::DIRTY_SIMULATOR_ID;
|
||||||
|
|
||||||
class EntitySimulation : public QObject {
|
class EntitySimulation : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -86,7 +86,7 @@ void EntityTree::postAddEntity(EntityItem* entity) {
|
||||||
emit addingEntity(entity->getEntityItemID());
|
emit addingEntity(entity->getEntityItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool allowLockChange) {
|
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||||
if (!containingElement) {
|
if (!containingElement) {
|
||||||
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
|
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
|
||||||
|
@ -99,22 +99,34 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateEntityWithElement(existingEntity, properties, containingElement, allowLockChange);
|
return updateEntityWithElement(existingEntity, properties, containingElement, senderNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties, bool allowLockChange) {
|
bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||||
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
|
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
|
||||||
if (!containingElement) {
|
if (!containingElement) {
|
||||||
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
|
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
|
||||||
<< entity->getEntityItemID();
|
<< entity->getEntityItemID();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return updateEntityWithElement(entity, properties, containingElement, allowLockChange);
|
return updateEntityWithElement(entity, properties, containingElement, senderNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& origProperties,
|
bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& origProperties,
|
||||||
EntityTreeElement* containingElement, bool allowLockChange) {
|
EntityTreeElement* containingElement, const SharedNodePointer& senderNode) {
|
||||||
EntityItemProperties properties = origProperties;
|
EntityItemProperties properties = origProperties;
|
||||||
|
|
||||||
|
bool allowLockChange;
|
||||||
|
QUuid senderID;
|
||||||
|
if (senderNode.isNull()) {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
allowLockChange = nodeList->getThisNodeCanAdjustLocks();
|
||||||
|
senderID = nodeList->getSessionUUID();
|
||||||
|
} else {
|
||||||
|
allowLockChange = senderNode->getCanAdjustLocks();
|
||||||
|
senderID = senderNode->getUUID();
|
||||||
|
}
|
||||||
|
|
||||||
if (!allowLockChange && (entity->getLocked() != properties.getLocked())) {
|
if (!allowLockChange && (entity->getLocked() != properties.getLocked())) {
|
||||||
qCDebug(entities) << "Refusing disallowed lock adjustment.";
|
qCDebug(entities) << "Refusing disallowed lock adjustment.";
|
||||||
return false;
|
return false;
|
||||||
|
@ -134,22 +146,41 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (properties.simulatorIDChanged() &&
|
if (getIsServer()) {
|
||||||
!entity->getSimulatorID().isNull() &&
|
bool simulationBlocked = !entity->getSimulatorID().isNull();
|
||||||
properties.getSimulatorID() != entity->getSimulatorID()) {
|
if (properties.simulatorIDChanged()) {
|
||||||
// A Node is trying to take ownership of the simulation of this entity from another Node. Only allow this
|
QUuid submittedID = properties.getSimulatorID();
|
||||||
// if ownership hasn't recently changed.
|
// a legit interface will only submit their own ID or NULL:
|
||||||
if (usecTimestampNow() - entity->getSimulatorIDChangedTime() < SIMULATOR_CHANGE_LOCKOUT_PERIOD) {
|
if (submittedID.isNull()) {
|
||||||
qCDebug(entities) << "simulator_change_lockout_period:"
|
if (entity->getSimulatorID() == senderID) {
|
||||||
<< entity->getSimulatorID() << "to" << properties.getSimulatorID();
|
// We only allow the simulation owner to clear their own simulationID's.
|
||||||
|
simulationBlocked = false;
|
||||||
|
}
|
||||||
|
// else: We assume the sender really did believe it was the simulation owner when it sent
|
||||||
|
} else if (submittedID == senderID) {
|
||||||
|
// the sender is trying to take or continue ownership
|
||||||
|
if (entity->getSimulatorID().isNull() || entity->getSimulatorID() == senderID) {
|
||||||
|
simulationBlocked = false;
|
||||||
|
} else {
|
||||||
|
// the sender is trying to steal ownership from another simulator
|
||||||
|
// so we apply the ownership change filter
|
||||||
|
if (usecTimestampNow() - entity->getSimulatorIDChangedTime() > SIMULATOR_CHANGE_LOCKOUT_PERIOD) {
|
||||||
|
simulationBlocked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the entire update is suspect --> ignore it
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (simulationBlocked) {
|
||||||
// squash the physics-related changes.
|
// squash the physics-related changes.
|
||||||
properties.setSimulatorIDChanged(false);
|
properties.setSimulatorIDChanged(false);
|
||||||
properties.setPositionChanged(false);
|
properties.setPositionChanged(false);
|
||||||
properties.setRotationChanged(false);
|
properties.setRotationChanged(false);
|
||||||
} else {
|
|
||||||
qCDebug(entities) << "allowing simulatorID change";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// else client accepts what the server says
|
||||||
|
|
||||||
QString entityScriptBefore = entity->getScript();
|
QString entityScriptBefore = entity->getScript();
|
||||||
uint32_t preFlags = entity->getDirtyFlags();
|
uint32_t preFlags = entity->getDirtyFlags();
|
||||||
|
@ -664,7 +695,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
||||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
|
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
|
||||||
qCDebug(entities) << " properties:" << properties;
|
qCDebug(entities) << " properties:" << properties;
|
||||||
}
|
}
|
||||||
updateEntity(entityItemID, properties, senderNode->getCanAdjustLocks());
|
updateEntity(entityItemID, properties, senderNode);
|
||||||
existingEntity->markAsChangedOnServer();
|
existingEntity->markAsChangedOnServer();
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "User attempted to edit an unknown entity. ID:" << entityItemID;
|
qCDebug(entities) << "User attempted to edit an unknown entity. ID:" << entityItemID;
|
||||||
|
|
|
@ -88,10 +88,10 @@ public:
|
||||||
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
|
||||||
// use this method if you only know the entityID
|
// use this method if you only know the entityID
|
||||||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool allowLockChange);
|
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||||
|
|
||||||
// use this method if you have a pointer to the entity (avoid an extra entity lookup)
|
// use this method if you have a pointer to the entity (avoid an extra entity lookup)
|
||||||
bool updateEntity(EntityItem* entity, const EntityItemProperties& properties, bool allowLockChange);
|
bool updateEntity(EntityItem* entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||||
|
|
||||||
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
|
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
|
||||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
|
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
|
||||||
|
@ -178,7 +178,8 @@ private:
|
||||||
|
|
||||||
void processRemovedEntities(const DeleteEntityOperator& theOperator);
|
void processRemovedEntities(const DeleteEntityOperator& theOperator);
|
||||||
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
|
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
|
||||||
EntityTreeElement* containingElement, bool allowLockChange);
|
EntityTreeElement* containingElement,
|
||||||
|
const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
||||||
|
|
|
@ -35,8 +35,9 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItem* entity
|
||||||
_serverGravity(0.0f),
|
_serverGravity(0.0f),
|
||||||
_serverAcceleration(0.0f),
|
_serverAcceleration(0.0f),
|
||||||
_accelerationNearlyGravityCount(0),
|
_accelerationNearlyGravityCount(0),
|
||||||
_shouldClaimSimulationOwnership(false),
|
_touchesOurSimulation(false),
|
||||||
_movingStepsWithoutSimulationOwner(0)
|
_framesSinceSimulatorBid(0),
|
||||||
|
_movingFramesWithoutSimulationOwner(0)
|
||||||
{
|
{
|
||||||
_type = MOTION_STATE_TYPE_ENTITY;
|
_type = MOTION_STATE_TYPE_ENTITY;
|
||||||
assert(entity != nullptr);
|
assert(entity != nullptr);
|
||||||
|
@ -60,6 +61,15 @@ void EntityMotionState::updateServerPhysicsVariables(uint32_t flags) {
|
||||||
if (flags & EntityItem::DIRTY_ANGULAR_VELOCITY) {
|
if (flags & EntityItem::DIRTY_ANGULAR_VELOCITY) {
|
||||||
_serverAngularVelocity = _entity->getAngularVelocity();
|
_serverAngularVelocity = _entity->getAngularVelocity();
|
||||||
}
|
}
|
||||||
|
if (flags & EntityItem::DIRTY_SIMULATOR_ID) {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
const QUuid& sessionID = nodeList->getSessionUUID();
|
||||||
|
if (_entity->getSimulatorID() != sessionID) {
|
||||||
|
_touchesOurSimulation = false;
|
||||||
|
_movingFramesWithoutSimulationOwner = 0;
|
||||||
|
_framesSinceSimulatorBid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
|
@ -143,19 +153,16 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
|
|
||||||
_entity->setLastSimulated(usecTimestampNow());
|
_entity->setLastSimulated(usecTimestampNow());
|
||||||
|
|
||||||
// if (_entity->getSimulatorID().isNull() && isMoving()) {
|
if (_entity->getSimulatorID().isNull()) {
|
||||||
if (_entity->getSimulatorID().isNull() && isMovingVsServer()) {
|
_movingFramesWithoutSimulationOwner++;
|
||||||
// if object is moving and has no owner, attempt to claim simulation ownership.
|
|
||||||
_movingStepsWithoutSimulationOwner++;
|
|
||||||
} else {
|
|
||||||
_movingStepsWithoutSimulationOwner = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ownershipClaimDelay = 50; // TODO -- how to pick this? based on meters from our characterController?
|
const uint32_t ownershipClaimDelay = 50; // TODO -- how to pick this? based on meters from our characterController?
|
||||||
|
if (_movingFramesWithoutSimulationOwner > ownershipClaimDelay) {
|
||||||
if (_movingStepsWithoutSimulationOwner > ownershipClaimDelay) {
|
|
||||||
//qDebug() << "Warning -- claiming something I saw moving." << getName();
|
//qDebug() << "Warning -- claiming something I saw moving." << getName();
|
||||||
setShouldClaimSimulationOwnership(true);
|
_touchesOurSimulation = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_movingFramesWithoutSimulationOwner = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
|
@ -177,8 +184,12 @@ void EntityMotionState::computeObjectShapeInfo(ShapeInfo& shapeInfo) {
|
||||||
// we alwasy resend packets for objects that have stopped moving up to some max limit.
|
// we alwasy resend packets for objects that have stopped moving up to some max limit.
|
||||||
const int MAX_NUM_NON_MOVING_UPDATES = 5;
|
const int MAX_NUM_NON_MOVING_UPDATES = 5;
|
||||||
|
|
||||||
bool EntityMotionState::doesNotNeedToSendUpdate() const {
|
bool EntityMotionState::doesNotNeedToSendUpdate(const QUuid& sessionID) const {
|
||||||
return !_body || (_body->isActive() && _numNonMovingUpdates > MAX_NUM_NON_MOVING_UPDATES);
|
if (!_body || !_entity) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sessionID != _entity->getSimulatorID() && !_touchesOurSimulation);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
@ -191,6 +202,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
_serverVelocity = bulletToGLM(_body->getLinearVelocity());
|
_serverVelocity = bulletToGLM(_body->getLinearVelocity());
|
||||||
_serverAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
_serverAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
||||||
_lastStep = simulationStep;
|
_lastStep = simulationStep;
|
||||||
|
_sentMoving = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,19 +214,26 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
|
||||||
int numSteps = simulationStep - _lastStep;
|
int numSteps = simulationStep - _lastStep;
|
||||||
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
_lastStep = simulationStep;
|
|
||||||
bool isActive = _body->isActive();
|
|
||||||
|
|
||||||
const float NON_MOVING_UPDATE_PERIOD = 1.0f;
|
const float NON_MOVING_UPDATE_PERIOD = 1.0f;
|
||||||
if (_sentMoving) {
|
if (!_sentMoving) {
|
||||||
|
// we resend non-moving update every NON_MOVING_UPDATE_PERIOD
|
||||||
|
// until it is removed from the outgoing updates
|
||||||
|
// (which happens when we don't own the simulation and it isn't touching our simulation)
|
||||||
|
return (dt > NON_MOVING_UPDATE_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isActive = _body->isActive();
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
// object has gone inactive but our last send was moving --> send non-moving update immediately
|
// object has gone inactive but our last send was moving --> send non-moving update immediately
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (dt > NON_MOVING_UPDATE_PERIOD && _numNonMovingUpdates < MAX_NUM_NON_MOVING_UPDATES) {
|
|
||||||
// RELIABLE_SEND_HACK: since we're not yet using a reliable method for non-moving update packets
|
_lastStep = simulationStep;
|
||||||
// we repeat these at the the MAX period above, and only send a limited number of them.
|
if (glm::length2(_serverVelocity) > 0.0f) {
|
||||||
return true;
|
_serverVelocity += _serverAcceleration * dt;
|
||||||
|
_serverVelocity *= powf(1.0f - _body->getLinearDamping(), dt);
|
||||||
|
_serverPosition += dt * _serverVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
||||||
|
@ -222,15 +241,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
|
||||||
// NOTE: math is done in the simulation-frame, which is NOT necessarily the same as the world-frame
|
// NOTE: math is done in the simulation-frame, which is NOT necessarily the same as the world-frame
|
||||||
// due to _worldOffset.
|
// due to _worldOffset.
|
||||||
|
// TODO: compensate for _worldOffset offset here
|
||||||
|
|
||||||
// compute position error
|
// compute position error
|
||||||
if (glm::length2(_serverVelocity) > 0.0f) {
|
|
||||||
_serverVelocity += _serverAcceleration * dt;
|
|
||||||
_serverVelocity *= powf(1.0f - _body->getLinearDamping(), dt);
|
|
||||||
_serverPosition += dt * _serverVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: compensate for simulation offset here
|
|
||||||
btTransform worldTrans = _body->getWorldTransform();
|
btTransform worldTrans = _body->getWorldTransform();
|
||||||
glm::vec3 position = bulletToGLM(worldTrans.getOrigin());
|
glm::vec3 position = bulletToGLM(worldTrans.getOrigin());
|
||||||
|
|
||||||
|
@ -283,31 +297,36 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT);
|
return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame, const QUuid& sessionID) {
|
||||||
if (!_entity || !remoteSimulationOutOfSync(simulationFrame)) {
|
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||||
|
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||||
|
assert(_entity);
|
||||||
|
assert(_body);
|
||||||
|
|
||||||
|
if (!remoteSimulationOutOfSync(simulationFrame)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getShouldClaimSimulationOwnership()) {
|
if (_entity->getSimulatorID() == sessionID) {
|
||||||
|
// we own the simulation
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
const uint32_t FRAMES_BETWEEN_OWNERSHIP_CLAIMS = 30;
|
||||||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
if (_touchesOurSimulation) {
|
||||||
const QUuid& simulatorID = _entity->getSimulatorID();
|
++_framesSinceSimulatorBid;
|
||||||
|
if (_framesSinceSimulatorBid > FRAMES_BETWEEN_OWNERSHIP_CLAIMS) {
|
||||||
|
// we don't own the simulation, but it's time to bid for it
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (simulatorID != myNodeID) {
|
|
||||||
// some other Node owns the simulating of this, so don't broadcast the results of local simulation.
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) {
|
||||||
if (!_entity || !_entity->isKnownID()) {
|
assert(_entity);
|
||||||
return; // never update entities that are unknown
|
assert(_entity->isKnownID());
|
||||||
}
|
|
||||||
|
|
||||||
bool active = _body->isActive();
|
bool active = _body->isActive();
|
||||||
if (!active) {
|
if (!active) {
|
||||||
|
@ -350,11 +369,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
_serverAcceleration = _entity->getAcceleration();
|
_serverAcceleration = _entity->getAcceleration();
|
||||||
_serverAngularVelocity = _entity->getAngularVelocity();
|
_serverAngularVelocity = _entity->getAngularVelocity();
|
||||||
|
|
||||||
_sentMoving = _serverVelocity != glm::vec3(0.0f) || _serverAngularVelocity != glm::vec3(0.0f);
|
_sentMoving = _serverVelocity != glm::vec3(0.0f) || _serverAngularVelocity != _serverVelocity || _serverAcceleration != _serverVelocity;
|
||||||
|
|
||||||
EntityItemProperties properties = _entity->getProperties();
|
EntityItemProperties properties = _entity->getProperties();
|
||||||
|
|
||||||
// explicitly set the properties that changed
|
// explicitly set the properties that changed so that they will be packed
|
||||||
properties.setPosition(_serverPosition);
|
properties.setPosition(_serverPosition);
|
||||||
properties.setRotation(_serverRotation);
|
properties.setRotation(_serverRotation);
|
||||||
properties.setVelocity(_serverVelocity);
|
properties.setVelocity(_serverVelocity);
|
||||||
|
@ -386,23 +405,20 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
properties.setLastEdited(_entity->getLastEdited());
|
properties.setLastEdited(_entity->getLastEdited());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
if (sessionID == _entity->getSimulatorID()) {
|
||||||
QUuid myNodeID = nodeList->getSessionUUID();
|
// we think we own the simulation
|
||||||
QUuid simulatorID = _entity->getSimulatorID();
|
if (!_sentMoving) {
|
||||||
|
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID
|
||||||
if (getShouldClaimSimulationOwnership()) {
|
// but we remember that we do still own it... and rely on the server to tell us that we don't
|
||||||
// we think we should own it, so we tell the server that we do,
|
|
||||||
// but we don't remember that we own it...
|
|
||||||
// instead we expect the sever to tell us later whose ownership it has accepted
|
|
||||||
properties.setSimulatorID(myNodeID);
|
|
||||||
setShouldClaimSimulationOwnership(false);
|
|
||||||
} else if (simulatorID == myNodeID
|
|
||||||
&& !_sentMoving
|
|
||||||
&& _numNonMovingUpdates == MAX_NUM_NON_MOVING_UPDATES) {
|
|
||||||
// we own it, the entity has stopped, and we're sending the last non-moving update
|
|
||||||
// --> give up ownership
|
|
||||||
_entity->setSimulatorID(QUuid());
|
|
||||||
properties.setSimulatorID(QUuid());
|
properties.setSimulatorID(QUuid());
|
||||||
|
_touchesOurSimulation = false;
|
||||||
|
} else {
|
||||||
|
// explicitly set the property's simulatorID so that it is flagged as changed and will be packed
|
||||||
|
properties.setSimulatorID(sessionID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||||
|
properties.setSimulatorID(sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EntityItem::getSendPhysicsUpdates()) {
|
if (EntityItem::getSendPhysicsUpdates()) {
|
||||||
|
@ -451,7 +467,7 @@ QUuid EntityMotionState::getSimulatorID() const {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void EntityMotionState::bump() {
|
void EntityMotionState::bump() {
|
||||||
setShouldClaimSimulationOwnership(true);
|
_touchesOurSimulation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||||
|
|
|
@ -46,13 +46,11 @@ public:
|
||||||
|
|
||||||
virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo);
|
virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo);
|
||||||
|
|
||||||
bool doesNotNeedToSendUpdate() const;
|
// TODO: Andrew to rename doesNotNeedToSendUpdate()
|
||||||
|
bool doesNotNeedToSendUpdate(const QUuid& sessionID) const;
|
||||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||||
bool shouldSendUpdate(uint32_t simulationFrame);
|
bool shouldSendUpdate(uint32_t simulationFrame, const QUuid& sessionID);
|
||||||
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
|
void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step);
|
||||||
|
|
||||||
void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; }
|
|
||||||
bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; }
|
|
||||||
|
|
||||||
virtual uint32_t getAndClearIncomingDirtyFlags() const;
|
virtual uint32_t getAndClearIncomingDirtyFlags() const;
|
||||||
|
|
||||||
|
@ -109,8 +107,9 @@ protected:
|
||||||
glm::vec3 _measuredAcceleration;
|
glm::vec3 _measuredAcceleration;
|
||||||
|
|
||||||
quint8 _accelerationNearlyGravityCount;
|
quint8 _accelerationNearlyGravityCount;
|
||||||
bool _shouldClaimSimulationOwnership;
|
bool _touchesOurSimulation;
|
||||||
quint32 _movingStepsWithoutSimulationOwner;
|
uint32_t _framesSinceOwnershipBid;
|
||||||
|
uint32_t _movingFramesWithoutSimulationOwner;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityMotionState_h
|
#endif // hifi_EntityMotionState_h
|
||||||
|
|
|
@ -195,12 +195,22 @@ void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motio
|
||||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||||
EntityItem* entity = entityState->getEntity();
|
EntityItem* entity = entityState->getEntity();
|
||||||
if (entity) {
|
if (entity) {
|
||||||
|
if (entity->isKnownID()) {
|
||||||
_outgoingChanges.insert(entityState);
|
_outgoingChanges.insert(entityState);
|
||||||
|
}
|
||||||
_entitiesToSort.insert(entityState->getEntity());
|
_entitiesToSort.insert(entityState->getEntity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
const QUuid& sessionID = nodeList->getSessionUUID();
|
||||||
|
if (sessionID.isNull()) {
|
||||||
|
// no updates to send
|
||||||
|
_outgoingChanges.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// send outgoing packets
|
// send outgoing packets
|
||||||
uint32_t numSubsteps = _physicsEngine->getNumSubsteps();
|
uint32_t numSubsteps = _physicsEngine->getNumSubsteps();
|
||||||
if (_lastStepSendPackets != numSubsteps) {
|
if (_lastStepSendPackets != numSubsteps) {
|
||||||
|
@ -209,10 +219,10 @@ void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motio
|
||||||
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
||||||
while (stateItr != _outgoingChanges.end()) {
|
while (stateItr != _outgoingChanges.end()) {
|
||||||
EntityMotionState* state = *stateItr;
|
EntityMotionState* state = *stateItr;
|
||||||
if (state->doesNotNeedToSendUpdate()) {
|
if (state->doesNotNeedToSendUpdate(sessionID)) {
|
||||||
stateItr = _outgoingChanges.erase(stateItr);
|
stateItr = _outgoingChanges.erase(stateItr);
|
||||||
} else if (state->shouldSendUpdate(numSubsteps)) {
|
} else if (state->shouldSendUpdate(numSubsteps, sessionID)) {
|
||||||
state->sendUpdate(_entityPacketSender, numSubsteps);
|
state->sendUpdate(_entityPacketSender, sessionID, numSubsteps);
|
||||||
++stateItr;
|
++stateItr;
|
||||||
} else {
|
} else {
|
||||||
++stateItr;
|
++stateItr;
|
||||||
|
|
|
@ -147,6 +147,7 @@ void PhysicsEngine::deleteObjects(VectorOfMotionStates& objects) {
|
||||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||||
btRigidBody* body = object->getRigidBody();
|
btRigidBody* body = object->getRigidBody();
|
||||||
object->setRigidBody(nullptr);
|
object->setRigidBody(nullptr);
|
||||||
|
body->setMotionState(nullptr);
|
||||||
delete body;
|
delete body;
|
||||||
object->releaseShape();
|
object->releaseShape();
|
||||||
delete object;
|
delete object;
|
||||||
|
@ -161,6 +162,7 @@ void PhysicsEngine::deleteObjects(SetOfMotionStates& objects) {
|
||||||
|
|
||||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||||
object->setRigidBody(nullptr);
|
object->setRigidBody(nullptr);
|
||||||
|
body->setMotionState(nullptr);
|
||||||
delete body;
|
delete body;
|
||||||
object->releaseShape();
|
object->releaseShape();
|
||||||
delete object;
|
delete object;
|
||||||
|
|
|
@ -103,7 +103,7 @@ void EntityTests::entityTreeTests(bool verbose) {
|
||||||
|
|
||||||
properties.setPosition(newPosition);
|
properties.setPosition(newPosition);
|
||||||
|
|
||||||
tree.updateEntity(entityID, properties, true);
|
tree.updateEntity(entityID, properties);
|
||||||
|
|
||||||
float targetRadius = oneMeter * 2.0f;
|
float targetRadius = oneMeter * 2.0f;
|
||||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOrigin, targetRadius);
|
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOrigin, targetRadius);
|
||||||
|
@ -143,7 +143,7 @@ void EntityTests::entityTreeTests(bool verbose) {
|
||||||
|
|
||||||
properties.setPosition(newPosition);
|
properties.setPosition(newPosition);
|
||||||
|
|
||||||
tree.updateEntity(entityID, properties, true);
|
tree.updateEntity(entityID, properties);
|
||||||
|
|
||||||
float targetRadius = oneMeter * 2.0f;
|
float targetRadius = oneMeter * 2.0f;
|
||||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenter, targetRadius);
|
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenter, targetRadius);
|
||||||
|
|
Loading…
Reference in a new issue