From cf74dbe1dc72586db6cf95025102cc2e175f472c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 19 Jun 2015 10:16:22 -0700 Subject: [PATCH] partial progress toward sim ownership negotiations works well for just a few objects but fails for piles committing some debug stuff that will have to be torn out later --- interface/src/avatar/AvatarMotionState.cpp | 4 - interface/src/avatar/AvatarMotionState.h | 1 - libraries/entities/src/EntityItem.cpp | 98 ++++++++++--- libraries/entities/src/EntityItem.h | 25 +++- .../entities/src/EntityItemProperties.cpp | 95 ++++++++++++ libraries/entities/src/EntityTree.cpp | 1 + libraries/octree/src/OctreePacketData.h | 2 + libraries/physics/src/EntityMotionState.cpp | 137 ++++++++++++------ libraries/physics/src/EntityMotionState.h | 12 +- libraries/physics/src/ObjectMotionState.cpp | 6 +- libraries/physics/src/ObjectMotionState.h | 8 +- libraries/physics/src/PhysicsEngine.cpp | 21 ++- 12 files changed, 319 insertions(+), 91 deletions(-) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 06da384a94..b106ee6398 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -148,10 +148,6 @@ QUuid AvatarMotionState::getSimulatorID() const { return _avatar->getSessionUUID(); } -// virtual -void AvatarMotionState::bump() { -} - // virtual int16_t AvatarMotionState::computeCollisionGroup() { return COLLISION_GROUP_OTHER_AVATAR; diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 79a4d23179..ac813e764c 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -55,7 +55,6 @@ public: virtual const QUuid& getObjectID() const; virtual QUuid getSimulatorID() const; - virtual void bump(); void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 82a843ff48..888ca239e2 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -27,6 +27,8 @@ #include "EntityTree.h" #include "EntitySimulation.h" +const char* plankyBlock2 = "PlankyBlock46"; // adebug + const quint64 DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD = (quint64)(0.2f * USECS_PER_SECOND); const quint64 MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD = 2 * USECS_PER_SECOND; @@ -581,11 +583,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // rules that we expect the server to be using, so it is possible that we'll sometimes ignore // the incoming _simulatorID data (e.g. we might know something that the server does not... yet). + int requiredProperties = 1; // adebug uint8_t priority = 0; if (propertyFlags.getHasProperty(PROP_SIMULATOR_PRIORITY)) { int bytes = OctreePacketData::unpackDataFromBytes(dataAt, priority); dataAt += bytes; bytesRead += bytes; + requiredProperties *= 3; // adebug } QUuid id; @@ -593,32 +597,66 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef int bytes = OctreePacketData::unpackDataFromBytes(dataAt, id); dataAt += bytes; bytesRead += bytes; + requiredProperties *= 7; } - if (_simulatorID != id) { - // ownership has changed - auto nodeList = DependencyManager::get(); - if (_simulatorID == nodeList->getSessionUUID()) { - // we think we're the simulation owner but entity-server says otherwise - // we relenquish ownership only if we don't have MAX_SIMULATOR_PRIORITY - if (_simulatorPriority != MAX_SIMULATOR_PRIORITY) { - // we're losing simulation ownership + // adebug + if (requiredProperties == 3) { + std::cout << "adebug split properties for '" << _name.toStdString() << "' with priority only" << std::endl; // adebug + } + else if (requiredProperties == 7) { + std::cout << "adebug split properties for '" << _name.toStdString() << "' with id only" << std::endl; // adebug + } + + // TODO: refactor simulation ownership info to be a single property + if (requiredProperties == 3*7) { + if (_simulatorID != id) { + // ownership has changed + auto nodeList = DependencyManager::get(); + if (_simulatorID == nodeList->getSessionUUID()) { + // we think we're the simulation owner but entity-server says otherwise + // we relenquish ownership only if we don't have MAX_SIMULATOR_PRIORITY + if (_simulatorPriority != MAX_SIMULATOR_PRIORITY) { + // we're losing simulation ownership + if (_name == plankyBlock2) { + std::cout << "adebug lose ownership of '" << _name.toStdString() << "' to " << id.toString().toStdString() << " with priority " << int(priority) << std::endl; // adebug + } + _simulatorID = id; + _simulatorPriority = priority; + if (! (_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID)) { + if (_name == plankyBlock2) { + std::cout << "adebug setting DIRTY_SIMULATOR_ID while losing ownership" << std::endl; // adebug + } + } + _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; + } + } else { + auto nodeList = DependencyManager::get(); + if (id == nodeList->getSessionUUID()) { + if (_name == plankyBlock2) { + std::cout << "adebug gain ownership of '" << _name.toStdString() << "' id " << id.toString().toStdString() << " with priority " << int(priority) << std::endl; // adebug + } + } _simulatorID = id; _simulatorPriority = priority; + if (! (_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID)) { + if (_name == plankyBlock2) { + std::cout << "adebug setting DIRTY_SIMULATOR_ID with ownership of " << _simulatorID.toString().toStdString() << std::endl; // adebug + } + } _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; } - } else { - _simulatorID = id; - _simulatorPriority = priority; - _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; - } - } 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 priority - auto nodeList = DependencyManager::get(); - if (_simulatorID != nodeList->getSessionUUID()) { - _simulatorPriority = priority; + } 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 priority + auto nodeList = DependencyManager::get(); + if (_simulatorID != nodeList->getSessionUUID()) { + if (_name == plankyBlock2) { + std::cout << "adebug priority of '" << _name.toStdString() << "' changing from " << int(_simulatorPriority) << " to " << int(priority) << std::endl; // adebug + } + _simulatorPriority = priority; + } } } } else if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) { @@ -904,6 +942,11 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { } } +void EntityItem::clearDirtyFlags(uint32_t mask) { + // adebug TODO: move this back to header after done debugging + _dirtyFlags &= ~mask; +} + bool EntityItem::isMoving() const { return hasVelocity() || hasAngularVelocity(); } @@ -1039,6 +1082,9 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); if (somethingChanged) { + if (_name == plankyBlock2) { + std::cout << "adebug update for '" << _name.toStdString() << "'" << std::endl; // adebug + } uint64_t now = usecTimestampNow(); #ifdef WANT_DEBUG int elapsed = now - getLastEdited(); @@ -1398,6 +1444,9 @@ void EntityItem::updateCreated(uint64_t value) { } void EntityItem::setSimulatorPriority(uint8_t priority) { + if (_name == plankyBlock2) { + std::cout << "adebug setSimulatorPriority() for '" << _name.toStdString() << "' from " << int(_simulatorPriority) << " to " << int(priority) << std::endl; // adebug + } _simulatorPriority = priority; if (_simulatorPriority == MAX_SIMULATOR_PRIORITY) { // we always extend the the ownership expiry for MAX_SIMULATOR_PRIORITY @@ -1409,6 +1458,9 @@ void EntityItem::setSimulatorPriority(uint8_t priority) { void EntityItem::setSimulatorID(const QUuid& value) { if (_simulatorID != value) { + if (_name == plankyBlock2) { + std::cout << "adebug setSimulatorID for '" << _name.toStdString() << "' from " << _simulatorID.toString().toStdString() << " to " << value.toString().toStdString() << std::endl; // adebug + } _simulatorID = value; if (!_simulatorID.isNull()) { // Note: this logic only works well if _simulatorPriority is properly set before this point @@ -1421,6 +1473,9 @@ void EntityItem::setSimulatorID(const QUuid& value) { void EntityItem::updateSimulatorID(const QUuid& value) { if (_simulatorID != value) { + if (_name == plankyBlock2) { + std::cout << "adebug updateSimulatorID for '" << _name.toStdString() << "' from " << _simulatorID.toString().toStdString() << " to " << value.toString().toStdString() << std::endl; // adebug + } _simulatorID = value; if (!_simulatorID.isNull()) { // Note: this logic only works well if _simulatorPriority is properly set before this point @@ -1433,6 +1488,9 @@ void EntityItem::updateSimulatorID(const QUuid& value) { } void EntityItem::clearSimulationOwnership() { + if (_name == plankyBlock2) { + std::cout << "adebug clearSimulationOwnership for '" << _name.toStdString() << "'" << std::endl; // adebug + } _simulatorPriority = 0; _simulatorID = QUuid(); _simulationOwnershipExpiry = 0; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 77531c59cf..2c62588913 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -72,6 +72,28 @@ const uint8_t ATTACHMENT_SIMULATOR_PRIORITY = MAX_SIMULATOR_PRIORITY; #define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10)) #define debugTreeVector(V) V << "[" << V << " in meters ]" +class SimulationOwner { +public: + SimulationOwner() : _id(), _priority(0) {} + SimulationOwner(const QUuid& id, uint8_t priority) : _id(id), _priority(priority) {} + + const QUuid& getID() const { return _id; } + uint8_t getPriority() const { return _priority; } + + void clear() { _id = QUuid(); _priority = 0; } + void set(const QUuid& id, uint8_t priority) { _id = id; _priority = priority; } + + bool isNull() const { return _id.isNull(); } + bool matchesID(const QUuid& id) const { return _id == id; } + //void toQByteArray(); + + bool operator>=(uint8_t priority) const { return _priority >= priority; } + +private: + QUuid _id; + uint8_t _priority; +}; + /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate @@ -367,7 +389,8 @@ public: virtual void updateShapeType(ShapeType type) { /* do nothing */ } uint32_t getDirtyFlags() const { return _dirtyFlags; } - void clearDirtyFlags(uint32_t mask = 0xffffffff) { _dirtyFlags &= ~mask; } + //void clearDirtyFlags(uint32_t mask = 0xffffffff) { _dirtyFlags &= ~mask; } + void clearDirtyFlags(uint32_t mask = 0xffffffff); bool isMoving() const; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4003288700..8d9b703874 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -325,6 +325,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked); CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures); CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData); + // TODO: combine these as one property CHECK_PROPERTY_CHANGE(PROP_SIMULATOR_PRIORITY, simulatorPriority); CHECK_PROPERTY_CHANGE(PROP_SIMULATOR_ID, simulatorID); CHECK_PROPERTY_CHANGE(PROP_TEXT, text); @@ -709,6 +710,42 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem // PROP_PAGED_PROPERTY, // PROP_CUSTOM_PROPERTIES_INCLUDED, +/* TODO: remove this old experiment code + // simulation ownership data needs to get back ASAP, and affects whether the "terse update" + // data will be accepted at the receiving end, so we put it at the front. +// if (requestedProperties.getHasProperty(PROP_SIMULATOR_PRIORITY) && +// requestedProperties.getHasProperty(PROP_SIMULATOR_ID)) { +// QByteArray ownershipData = properties.getSimulatorID().toRfc4122(); +// ownershipData.append(properties.getSimulatorPriority(); +// LevelDetails propertyLevel = packetData->startLevel(); +// if (packetData->appendRawData(ownershipData)) { +// propertyFlags |= PROP_SIMULATOR_PRIORITY; +// propertiesDidntFit -= PROP_SIMULATOR_PRIORITY; +// propertyCount++; +// +// propertyFlags |= PROP_SIMULATOR_ID; +// propertiesDidntFit -= PROP_SIMULATOR_ID; +// propertyCount++; +// +// packetData->endLevel(propertyLevel); +// } +// } + // BOOKMARK -- replace the two ownership properties with one... at the EntityProperties level + // but make it two properties at the EntityItem + if (requestedProperties.getHasProperty(PROP_SIMULATOR_OWNERSHIP)) { + QByteArray ownershipData = properties.getSimulatorID().toRfc4122(); + ownershipData.append(properties.getSimulatorPriority(); + LevelDetails propertyLevel = packetData->startLevel(); + if (packetData->appendRawData(ownershipData)) { + propertyFlags |= PROP_SIMULATOR_OWNERSHIP; + propertyCount++; + packetData->endLevel(propertyLevel); + } else { + propertiesDidntFit -= PROP_SIMULATOR_OWNERSHIP; + } + } + */ + APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition()); APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation()); @@ -733,6 +770,64 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData()); APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_PRIORITY, properties.getSimulatorPriority()); APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, properties.getSimulatorID()); +/* TODO remove this experiment too + if (requestedProperties.getHasProperty(PROP_SIMULATOR_PRIORITY) && + requestedProperties.getHasProperty(PROP_SIMULATOR_ID)) { + + QByteArray bytes = properties.getSimulatorID().toRfc4122(); + if (packetData->canAppendBytes(1 + bytes.size())) { + APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_PRIORITY, properties.getSimulatorPriority()); + APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, properties.getSimulatorID()); + } else { + LevelDetails propertyLevel = packetData->startLevel(); + successPropertyFits = false; + packetData->discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } + if (!requestedProperties.getHasProperty(PROP_SIMULATOR_PRIORITY)) { + propertiesDidntFit -= PROP_SIMULATOR_PRIORITY; + } + if (!requestedProperties.getHasProperty(PROP_SIMULATOR_ID)) { + propertiesDidntFit -= PROP_SIMULATOR_ID; + } + } +*/ +/* and this one + //#define APPEND_ENTITY_PROPERTY(P,V) + if (requestedProperties.getHasProperty(PROP_SIMULATOR_PRIORITY) && + requestedProperties.getHasProperty(PROP_SIMULATOR_ID)) { + + LevelDetails propertyLevel = packetData->startLevel(); + + QByteArray bytes = properties.getSimulatorID().toRfc4122(); + if (packetData->canAppendBytes(10 + bytes.size())) { + packetData->appendValue(properties.getSimulatorPriority()); + propertyFlags |= PROP_SIMULATOR_PRIORITY; + propertiesDidntFit -= PROP_SIMULATOR_PRIORITY; + propertyCount++; + packetData->endLevel(propertyLevel); + + propertyLevel = packetData->startLevel(); + packetData->appendValue(properties.getSimulatorID()); + propertyFlags |= PROP_SIMULATOR_ID; + propertiesDidntFit -= PROP_SIMULATOR_ID; + propertyCount++; + packetData->endLevel(propertyLevel); + } else { + successPropertyFits = false; + packetData->discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + if (!requestedProperties.getHasProperty(PROP_SIMULATOR_PRIORITY)) { + propertiesDidntFit -= PROP_SIMULATOR_PRIORITY; + } + if (!requestedProperties.getHasProperty(PROP_SIMULATOR_ID)) { + propertiesDidntFit -= PROP_SIMULATOR_ID; + } + } +*/ APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ab981862a1..4a03b0a6df 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -203,6 +203,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI uint32_t newFlags = entity->getDirtyFlags() & ~preFlags; if (newFlags) { if (_simulation) { + std::cout << "adebug newFlags & DIRTY_SIMULATION_FLAGS = 0x" << std::hex << (newFlags & DIRTY_SIMULATION_FLAGS) << std::dec << std::endl; // adebug if (newFlags & DIRTY_SIMULATION_FLAGS) { _simulation->lock(); _simulation->changeEntity(entity); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 9476fe024e..1dc66b23ca 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -188,6 +188,8 @@ public: bool appendRawData(const unsigned char* data, int length); bool appendRawData(QByteArray data); + bool canAppendBytes(int numBytes) const { return _bytesAvailable > numBytes; } + /// returns a byte offset from beginning of the uncompressed stream based on offset from end. /// Positive offsetFromEnd returns that many bytes before the end of uncompressed stream int getUncompressedByteOffset(int offsetFromEnd = 0) const { return _bytesInUse - offsetFromEnd; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 804890bf20..f2f5991124 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -22,12 +22,14 @@ #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS #include "EntityTree.h" #endif +const char* plankyBlock = "PlankyBlock46"; // adebug static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; const uint32_t LOOPS_FOR_SIMULATION_ORPHAN = 50; -const uint32_t LOOPS_BETWEEN_OWNERSHIP_BIDS = 30; +//const uint32_t LOOPS_BETWEEN_OWNERSHIP_BIDS = 30; +const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS bool EntityMotionState::entityTreeIsLocked() const { @@ -69,8 +71,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _serverGravity(0.0f), _serverAcceleration(0.0f), _accelerationNearlyGravityCount(0), - _candidateForOwnership(false), - _loopsSinceOwnershipBid(0), + _nextOwnershipBid(0), _loopsWithoutOwner(0) { _type = MOTIONSTATE_TYPE_ENTITY; @@ -94,37 +95,55 @@ void EntityMotionState::updateServerPhysicsVariables() { } // virtual -void EntityMotionState::handleEasyChanges(uint32_t flags) { +void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { assert(entityTreeIsLocked()); + if (_entity && _entity->getName() == plankyBlock) { + quint64 dt = (usecTimestampNow() - _activationTime) / 1000; // adebug + std::cout << "adebug handleEasyChanges flags = 0x" << std::hex << flags << std::dec << " dt = " << dt << std::endl; // adebug + } updateServerPhysicsVariables(); - ObjectMotionState::handleEasyChanges(flags); + ObjectMotionState::handleEasyChanges(flags, engine); if (flags & EntityItem::DIRTY_SIMULATOR_ID) { - _loopsSinceOwnershipBid = 0; + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug handleEasyChanges() '" << _entity->getName().toStdString() << "' found DIRTY_SIMULATOR_ID flag" << std::endl; // adebug + } _loopsWithoutOwner = 0; - _candidateForOwnership = false; - if (_entity->getSimulatorID().isNull() - && !_entity->isMoving() - && _body->isActive()) { + if (_entity->getSimulatorID().isNull()) { + // simulation ownership is being removed // remove the ACTIVATION flag because this object is coming to rest // according to a remote simulation and we don't want to wake it up again flags &= ~EntityItem::DIRTY_PHYSICS_ACTIVATION; + // hint to Bullet that the object is deactivating _body->setActivationState(WANTS_DEACTIVATION); - } else { - auto nodeList = DependencyManager::get(); - const QUuid& sessionID = nodeList->getSessionUUID(); - if (_entity->getSimulatorID() != sessionID) { - _loopsSinceOwnershipBid = 0; + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug handleEasyChanges() '" << _entity->getName().toStdString() << "' clearing ownership so _candidatePriority goes to 0" << std::endl; // adebug + } + _candidatePriority = 0; + if (_expectedOwnership != -1) { + std::cout << "adebug unexpected loss of ownership '" << _entity->getName().toStdString() << "' expected -1 but got " << _expectedOwnership << std::endl; // adebug + } + _expectedOwnership = 0; + } else { + _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; + if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulatorPriority() > _candidatePriority) { + // we own the simulation or our priority looses to remote + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug handleEasyChanges() '" << _entity->getName().toStdString() << "' we own it so _candidatePriority goes to 0" << std::endl; // adebug + } + if (_expectedOwnership != 1) { + std::cout << "adebug unexpected gain of ownership '" << _entity->getName().toStdString() << "' expected 1 but got " << _expectedOwnership << " _candidatePriority = " << int(_candidatePriority) << std::endl; // adebug + } + _expectedOwnership = 0; + _candidatePriority = 0; } } } if (flags & EntityItem::DIRTY_SIMULATOR_OWNERSHIP) { + // also known as "bid for ownership with SCRIPT priority" // we're manipulating this object directly via script, so we artificially // manipulate the logic to trigger an immediate bid for ownership - _candidateForOwnership = true; - _loopsSinceOwnershipBid = LOOPS_BETWEEN_OWNERSHIP_BIDS; - _loopsWithoutOwner = LOOPS_FOR_SIMULATION_ORPHAN; - _simulatorPriorityHint = SCRIPT_EDIT_SIMULATOR_PRIORITY; + setSimulatorPriorityHint(SCRIPT_EDIT_SIMULATOR_PRIORITY); } if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { _body->activate(); @@ -202,13 +221,14 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { if (_entity->getSimulatorID().isNull()) { _loopsWithoutOwner++; - if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN) { + if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextOwnershipBid) { //qDebug() << "Warning -- claiming something I saw moving." << getName(); - _candidateForOwnership = true; - _loopsSinceOwnershipBid = LOOPS_BETWEEN_OWNERSHIP_BIDS + 1; + quint64 dt = (usecTimestampNow() - _activationTime) / 1000; // adebug + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug setWorldTransform() bid for orphan '" << _entity->getName().toStdString() << "' dt = " << dt << std::endl; // adebug + } + setSimulatorPriorityHint(VOLUNTEER_SIMULATOR_PRIORITY); } - } else { - _loopsWithoutOwner = 0; } #ifdef WANT_DEBUG @@ -239,7 +259,7 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { return false; } assert(entityTreeIsLocked()); - return _candidateForOwnership || sessionID == _entity->getSimulatorID(); + return _candidatePriority > 0 || sessionID == _entity->getSimulatorID(); } bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { @@ -356,18 +376,19 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s if (_entity->getSimulatorID() != sessionID) { // we don't own the simulation, but maybe we should... - if (_candidateForOwnership) { - _candidateForOwnership = false; - ++_loopsSinceOwnershipBid; - if (_loopsSinceOwnershipBid > LOOPS_BETWEEN_OWNERSHIP_BIDS) { - // it's time to bid for ownership - _loopsSinceOwnershipBid = 0; - return true; + if (_candidatePriority > 0) { + if (_candidatePriority < _entity->getSimulatorPriority()) { + // our priority looses to remote, so we don't bother to bid + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug shouldSendUpdate() '" << _entity->getName().toStdString() << "' clear priority " << int(_candidatePriority) << " in favor of remote priority " << int(_entity->getSimulatorPriority()) << std::endl; // adebug + } + _candidatePriority = 0; + return false; } + return usecTimestampNow() > _nextOwnershipBid; } return false; } - _candidateForOwnership = false; return remoteSimulationOutOfSync(simulationStep); } @@ -456,15 +477,28 @@ 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 + std::cout << "adebug releasing ownership of '" << _entity->getName().toStdString() << "' for inactivity" << std::endl; // adebug properties.clearSimulatorOwnership(); + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug sendUpdate() send clear ownership for '" << _entity->getName().toStdString() << "'" << std::endl; // adebug + } + _expectedOwnership = -1; } else { // re-assert the simulation info properties.setSimulatorOwnership(sessionID, _entity->getSimulatorPriority()); + _expectedOwnership = 0; } } else { // we don't own the simulation for this entity yet, but we're sending a bid for it - properties.setSimulatorOwnership(sessionID, glm::max(_simulatorPriorityHint, VOLUNTEER_SIMULATOR_PRIORITY)); - _simulatorPriorityHint = 0; + quint64 dt = (usecTimestampNow() - _activationTime) / 1000; // adebug + uint8_t bidPriority = glm::max(_candidatePriority, VOLUNTEER_SIMULATOR_PRIORITY); // adebug + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug sendUpdate() bid for ownership of '" << _entity->getName().toStdString() << "' dt = " << dt << " with priority " << int(bidPriority) << std::endl; // adebug + } + properties.setSimulatorOwnership(sessionID, glm::max(_candidatePriority, VOLUNTEER_SIMULATOR_PRIORITY)); + _nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS; + _expectedOwnership = 1; + //_candidatePriority = 0; // TODO: it would be nice to not have to clear this until we get a message back that ownership has changed } if (EntityItem::getSendPhysicsUpdates()) { @@ -502,6 +536,13 @@ uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { return dirtyFlags; } +// virtual +uint8_t EntityMotionState::getSimulatorPriority() const { + if (_entity) { + return _entity->getSimulatorPriority(); + } + return 0; +} // virtual QUuid EntityMotionState::getSimulatorID() const { @@ -512,10 +553,13 @@ QUuid EntityMotionState::getSimulatorID() const { return QUuid(); } - // virtual -void EntityMotionState::bump() { - _candidateForOwnership = true; +void EntityMotionState::bump(uint8_t priority) { + if (_entity) { + //uint8_t inheritedPriority = priority < 2 ? 1 : priority - 1; + uint8_t inheritedPriority = VOLUNTEER_SIMULATOR_PRIORITY; + setSimulatorPriorityHint(inheritedPriority); + } } void EntityMotionState::resetMeasuredBodyAcceleration() { @@ -543,9 +587,11 @@ void EntityMotionState::measureBodyAcceleration() { glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; _lastVelocity = velocity; - if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS && !_candidateForOwnership) { - // object has just been re-activated so clear ownership logic - _loopsSinceOwnershipBid = 0; + if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) { + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug measureBodyAcceleration() activate '" << _entity->getName().toStdString() << "'" << std::endl; // adebug + } + _activationTime = usecTimestampNow(); // adebug _loopsWithoutOwner = 0; _lastStep = ObjectMotionState::getWorldSimulationStep(); _sentInactive = false; @@ -589,6 +635,11 @@ int16_t EntityMotionState::computeCollisionGroup() { } void EntityMotionState::setSimulatorPriorityHint(uint8_t priority) { - _candidateForOwnership = true; - _simulatorPriorityHint = priority; + uint8_t oldPriority = _candidatePriority; + _candidatePriority = glm::max(_candidatePriority, priority); + if (_candidatePriority != oldPriority) { + if (_entity && _entity->getName() == plankyBlock) { + std::cout << "adebug setSimulatorPriorityHint() '" << _entity->getName().toStdString() << "' _candidatePrioity changed from " << int(oldPriority) << " to " << int(_candidatePriority) << std::endl; // adebug + } + } } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 109c041d93..34a8c70e30 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,7 +29,7 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); - virtual void handleEasyChanges(uint32_t flags); + virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem @@ -68,8 +68,9 @@ public: virtual const QUuid& getObjectID() const { return _entity->getID(); } + virtual uint8_t getSimulatorPriority() const; virtual QUuid getSimulatorID() const; - virtual void bump(); + virtual void bump(uint8_t priority); EntityItemPointer getEntity() const { return _entity; } @@ -113,10 +114,11 @@ protected: float _measuredDeltaTime; quint8 _accelerationNearlyGravityCount; - bool _candidateForOwnership; - uint32_t _loopsSinceOwnershipBid; + quint64 _nextOwnershipBid = 0; uint32_t _loopsWithoutOwner; - uint8_t _simulatorPriorityHint; + uint8_t _candidatePriority = 0; + quint64 _activationTime = 0; // adebug + int _expectedOwnership = 0; // adebug }; #endif // hifi_EntityMotionState_h diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 85e67d72f7..7ff119fb79 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -114,7 +114,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } -void ObjectMotionState::handleEasyChanges(uint32_t flags) { +void ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { if (flags & EntityItem::DIRTY_POSITION) { btTransform worldTrans; if (flags & EntityItem::DIRTY_ROTATION) { @@ -159,7 +159,7 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) { // no HARD flags remain, so do any EASY changes if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); + handleEasyChanges(flags, engine); } return; } @@ -174,7 +174,7 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* } } if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - handleEasyChanges(flags); + handleEasyChanges(flags, engine); } // it is possible that there are no HARD flags at this point (if DIRTY_SHAPE was removed) // so we check again befoe we reinsert: diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 502efb5f98..1ef3ac0dcd 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -40,7 +40,8 @@ enum MotionStateType { const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES | EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP | - EntityItem::DIRTY_MATERIAL | EntityItem::DIRTY_SIMULATOR_OWNERSHIP); + EntityItem::DIRTY_MATERIAL | EntityItem::DIRTY_SIMULATOR_ID | + EntityItem::DIRTY_SIMULATOR_OWNERSHIP); // These are the set of incoming flags that the PhysicsEngine needs to hear about: const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS | @@ -70,7 +71,7 @@ public: ObjectMotionState(btCollisionShape* shape); ~ObjectMotionState(); - virtual void handleEasyChanges(uint32_t flags); + virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); void updateBodyMaterialProperties(); @@ -118,8 +119,9 @@ public: virtual const QUuid& getObjectID() const = 0; + virtual uint8_t getSimulatorPriority() const { return 0; } virtual QUuid getSimulatorID() const = 0; - virtual void bump() = 0; + virtual void bump(uint8_t priority) {} virtual QString getName() { return ""; } diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 55fc5e6295..a200abede2 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -194,7 +194,7 @@ void PhysicsEngine::changeObjects(VectorOfMotionStates& objects) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) { object->handleHardAndEasyChanges(flags, this); } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - object->handleEasyChanges(flags); + object->handleEasyChanges(flags, this); } } } @@ -260,9 +260,6 @@ void PhysicsEngine::stepSimulation() { void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { BT_PROFILE("ownershipInfection"); - if (_sessionID.isNull()) { - return; - } const btCollisionObject* characterObject = _characterController ? _characterController->getCollisionObject() : nullptr; @@ -272,14 +269,14 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const if (b && ((a && a->getSimulatorID() == _sessionID && !objectA->isStaticObject()) || (objectA == characterObject))) { // NOTE: we might own the simulation of a kinematic object (A) // but we don't claim ownership of kinematic objects (B) based on collisions here. - if (!objectB->isStaticOrKinematicObject()) { - b->bump(); + if (!objectB->isStaticOrKinematicObject() && b->getSimulatorID() != _sessionID) { + b->bump(a->getSimulatorPriority()); } } else if (a && ((b && b->getSimulatorID() == _sessionID && !objectB->isStaticObject()) || (objectB == characterObject))) { // SIMILARLY: we might own the simulation of a kinematic object (B) // but we don't claim ownership of kinematic objects (A) based on collisions here. - if (!objectA->isStaticOrKinematicObject()) { - a->bump(); + if (!objectA->isStaticOrKinematicObject() && a->getSimulatorID() != _sessionID) { + a->bump(b->getSimulatorPriority()); } } } @@ -311,7 +308,9 @@ void PhysicsEngine::updateContactMap() { _contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0)); } - doOwnershipInfection(objectA, objectB); + if (!_sessionID.isNull()) { + doOwnershipInfection(objectA, objectB); + } } } } @@ -402,7 +401,7 @@ void PhysicsEngine::bump(ObjectMotionState* motionState) { if (!objectA->isStaticOrKinematicObject()) { ObjectMotionState* motionStateA = static_cast(objectA->getUserPointer()); if (motionStateA) { - motionStateA->bump(); + motionStateA->bump(VOLUNTEER_SIMULATOR_PRIORITY); objectA->setActivationState(ACTIVE_TAG); } } @@ -410,7 +409,7 @@ void PhysicsEngine::bump(ObjectMotionState* motionState) { if (!objectB->isStaticOrKinematicObject()) { ObjectMotionState* motionStateB = static_cast(objectB->getUserPointer()); if (motionStateB) { - motionStateB->bump(); + motionStateB->bump(VOLUNTEER_SIMULATOR_PRIORITY); objectB->setActivationState(ACTIVE_TAG); } }