From 12fc18092bacce492be6f0eccc682900a55d6485 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 23 Jun 2015 16:19:57 -0700 Subject: [PATCH] SimulationOwner as one property --- libraries/entities/src/EntityItem.cpp | 201 ++++++------------ libraries/entities/src/EntityItem.h | 43 +--- .../entities/src/EntityItemProperties.cpp | 161 ++++---------- libraries/entities/src/EntityItemProperties.h | 13 +- libraries/entities/src/EntityPropertyFlags.h | 4 +- .../entities/src/EntityScriptingInterface.cpp | 21 +- libraries/entities/src/EntityTree.cpp | 20 +- libraries/entities/src/SimulationOwner.cpp | 188 ++++++++++++++++ libraries/entities/src/SimulationOwner.h | 78 +++++++ libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/octree/src/OctreePacketData.h | 1 + libraries/physics/src/EntityMotionState.cpp | 35 +-- 12 files changed, 435 insertions(+), 332 deletions(-) create mode 100644 libraries/entities/src/SimulationOwner.cpp create mode 100644 libraries/entities/src/SimulationOwner.h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 888ca239e2..9752c46aed 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include // adebug +#include // adebug + #include #include @@ -69,9 +72,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _collisionsWillMove(ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE), _locked(ENTITY_ITEM_DEFAULT_LOCKED), _userData(ENTITY_ITEM_DEFAULT_USER_DATA), - _simulatorPriority(0), - _simulatorID(ENTITY_ITEM_DEFAULT_SIMULATOR_ID), - _simulationOwnershipExpiry(0), + _simulationOwner(), _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), _name(ENTITY_ITEM_DEFAULT_NAME), _href(""), @@ -102,6 +103,7 @@ EntityItem::~EntityItem() { EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties; + requestedProperties += PROP_SIMULATION_OWNER; requestedProperties += PROP_POSITION; requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete requestedProperties += PROP_ROTATION; @@ -126,8 +128,6 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_USER_DATA; requestedProperties += PROP_MARKETPLACE_ID; requestedProperties += PROP_NAME; - requestedProperties += PROP_SIMULATOR_PRIORITY; - requestedProperties += PROP_SIMULATOR_ID; requestedProperties += PROP_HREF; requestedProperties += PROP_DESCRIPTION; @@ -235,6 +235,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet // PROP_PAGED_PROPERTY, // PROP_CUSTOM_PROPERTIES_INCLUDED, + APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, _simulationOwner.toByteArray()); APPEND_ENTITY_PROPERTY(PROP_POSITION, getPosition()); APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); // NOTE: PROP_RADIUS obsolete APPEND_ENTITY_PROPERTY(PROP_ROTATION, getRotation()); @@ -256,8 +257,6 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, getCollisionsWillMove()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); - APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_PRIORITY, getSimulatorPriority()); - APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, getSimulatorID()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); @@ -328,6 +327,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) { + static quint64 maxSkipTime = 0; // adebug if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { @@ -359,7 +359,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef glm::vec3 saveAngularVelocity = _angularVelocity; int originalLength = bytesLeftToRead; - QByteArray originalDataBuffer((const char*)data, originalLength); + // TODO: figure out a way to avoid the big deep copy below. + QByteArray originalDataBuffer((const char*)data, originalLength); // big deep copy! int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; @@ -443,6 +444,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef #endif bool ignoreServerPacket = false; // assume we'll use this server packet + std::ostringstream debugOutput; // If this packet is from the same server edit as the last packet we accepted from the server // we probably want to use it. @@ -451,6 +453,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // the most recent packet from this server time if (_lastEdited > _lastEditedFromRemote) { ignoreServerPacket = true; + } else { + debugOutput << "adebug fromSameServerEdit for '" << _name.toStdString() << "' le - lefr = " << (_lastEdited - _lastEditedFromRemote) << std::flush; // adebug } } else { // If this isn't from the same sever packet, then honor our skew adjusted times... @@ -458,6 +462,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // then we will not be changing our values, instead we just read and skip the data if (_lastEdited > lastEditedFromBufferAdjusted) { ignoreServerPacket = true; + } else { + debugOutput << "adebug honor skew adjust for '" << _name.toStdString() << "' le - lefba = " << (_lastEdited - lastEditedFromBufferAdjusted) << std::flush; // adebug } } @@ -537,6 +543,35 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef EntityPropertyFlags propertyFlags = encodedPropertyFlags; dataAt += propertyFlags.getEncodedLength(); bytesRead += propertyFlags.getEncodedLength(); + + if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER) { + // NOTE: the server is authoritative for changes to simOwnerID so we always unpack this data, + // even when we would otherwise ignore the rest of the packet. That said, we assert the priority + // 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). + + // BOOKMARK TODO adebug: follow pattern where the class unpacks itself + if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { + QByteArray simOwnerData; + int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData); + SimulationOwner newSimOwner; + newSimOwner.fromByteArray(simOwnerData); + dataAt += bytes; + bytesRead += bytes; + + SimulationOwner oldOwner = _simulationOwner; // adebug + if (_simulationOwner.set(newSimOwner)) { + std::cout << "adebug something changed: owner = " << _simulationOwner << std::endl; // adebug + _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; + } + if (oldOwner != _simulationOwner) { + std::cout << "adebug ownership changed from " << oldOwner.getID().toString().toStdString() << ":" << int(oldOwner.getPriority()) << " to " + << _simulationOwner.getID().toString().toStdString() << ":" << int(_simulationOwner.getPriority()) + << std::endl; // adebug + } + } + } + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); // Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS @@ -577,89 +612,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); - if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATOR_PRIORITY) { - // NOTE: the server is authoritative for changes to _simulatorID so we always unpack this data, - // even when we would otherwise ignore the rest of the packet. That said, we assert the priority - // 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; - if (propertyFlags.getHasProperty(PROP_SIMULATOR_ID)) { - int bytes = OctreePacketData::unpackDataFromBytes(dataAt, id); - dataAt += bytes; - bytesRead += bytes; - requiredProperties *= 7; - } - - // 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 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) { + if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION && + args.bitstreamVersion < VERSION_ENTITIES_HAVE_SIMULATION_OWNER) { // we always accept the server's notion of simulatorID, so we fake overwriteLocalData as true // before we try to READ_ENTITY_PROPERTY it bool temp = overwriteLocalData; @@ -698,13 +652,17 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // use our simulation helper routine to get a best estimate of where the entity should be. const float MIN_TIME_SKIP = 0.0f; const float MAX_TIME_SKIP = 1.0f; // in seconds + quint64 dt = now - lastSimulatedFromBufferAdjusted; + if (dt > maxSkipTime) { + maxSkipTime = dt; + std::cout << "adebug maxSkipTime = " << maxSkipTime << " for '" << _name.toStdString() << "'" << std::endl; // adebug + } float skipTimeForward = glm::clamp((float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND), MIN_TIME_SKIP, MAX_TIME_SKIP); if (skipTimeForward > 0.0f) { #ifdef WANT_DEBUG qCDebug(entities) << "skipTimeForward:" << skipTimeForward; #endif - // we want to extrapolate the motion forward to compensate for packet travel time, but // we don't want the side effect of flag setting. simulateKinematicMotion(skipTimeForward, false); @@ -714,7 +672,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef auto nodeList = DependencyManager::get(); const QUuid& myNodeID = nodeList->getSessionUUID(); if (overwriteLocalData) { - if (_simulatorID == myNodeID && !_simulatorID.isNull()) { + // TODO adebug: make this use operator==() + if (_simulationOwner.matchesID(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); @@ -723,6 +682,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef _angularVelocity = saveAngularVelocity; _dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES); } else { + std::cout << "adebug myNode = " << myNodeID.toString().toStdString() << " owner = " << _simulationOwner.getID().toString().toStdString() << std::endl; // adebug + std::cout << debugOutput.str() << std::endl; // adebug _lastSimulated = now; } } @@ -987,6 +948,7 @@ EntityItemProperties EntityItem::getProperties() const { properties._type = getType(); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulationOwner, getSimulationOwner); COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPosition); COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensions); // NOTE: radius is obsolete COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation); @@ -1012,8 +974,6 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove); COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked); COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorPriority, getSimulatorPriority); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorID, getSimulatorID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); @@ -1064,8 +1024,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove); SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorPriority, setSimulatorPriority); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, updateSimulatorID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner); // non-simulation properties below SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); @@ -1443,47 +1402,21 @@ 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 - _simulationOwnershipExpiry = usecTimestampNow() + MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD; - } else if (_simulatorPriority == 0) { - _simulationOwnershipExpiry = 0; - } +void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) { + _simulationOwner.set(id, 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 - quint64 lockoutPeriod = (_simulatorPriority == MAX_SIMULATOR_PRIORITY) - ? MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD : DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD; - _simulationOwnershipExpiry = usecTimestampNow() + lockoutPeriod; - } - } +void EntityItem::setSimulationOwner(const SimulationOwner& owner) { + _simulationOwner.set(owner); } 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 - quint64 lockoutPeriod = (_simulatorPriority == MAX_SIMULATOR_PRIORITY) - ? MAX_SIMULATOR_CHANGE_LOCKOUT_PERIOD : DEFAULT_SIMULATOR_CHANGE_LOCKOUT_PERIOD; - _simulationOwnershipExpiry = usecTimestampNow() + lockoutPeriod; - } + QUuid oldID = _simulationOwner.getID(); + if (_simulationOwner.setID(value)) { _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; + if (oldID != _simulationOwner.getID() && _name == plankyBlock2) { + std::cout << "adebug updateSimulatorID for '" << _name.toStdString() << "' from " << oldID.toString().toStdString() << " to " << value.toString().toStdString() << std::endl; // adebug + } } } @@ -1491,9 +1424,7 @@ void EntityItem::clearSimulationOwnership() { if (_name == plankyBlock2) { std::cout << "adebug clearSimulationOwnership for '" << _name.toStdString() << "'" << std::endl; // adebug } - _simulatorPriority = 0; - _simulatorID = QUuid(); - _simulationOwnershipExpiry = 0; + _simulationOwner.clear(); // 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; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 2c62588913..f657a746d8 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -29,6 +29,7 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesDefaults.h" #include "EntityTypes.h" +#include "SimulationOwner.h" class EntitySimulation; class EntityTreeElement; @@ -60,11 +61,6 @@ 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 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() { }; @@ -72,29 +68,6 @@ 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 /// one directly, instead you must only construct one of it's derived classes with additional features. @@ -345,13 +318,13 @@ public: const QString& getUserData() const { return _userData; } void setUserData(const QString& value) { _userData = value; } - void setSimulatorPriority(uint8_t priority); - uint8_t getSimulatorPriority() const { return _simulatorPriority; } + const SimulationOwner& getSimulationOwner() const { return _simulationOwner; } + void setSimulationOwner(const QUuid& id, quint8 priority); + void setSimulationOwner(const SimulationOwner& owner); - QUuid getSimulatorID() const { return _simulatorID; } - void setSimulatorID(const QUuid& value); + quint8 getSimulatorPriority() const { return _simulationOwner.getPriority(); } + QUuid getSimulatorID() const { return _simulationOwner.getID(); } void updateSimulatorID(const QUuid& value); - const quint64& getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; } void clearSimulationOwnership(); const QString& getMarketplaceID() const { return _marketplaceID; } @@ -460,9 +433,7 @@ protected: bool _collisionsWillMove; bool _locked; QString _userData; - uint8_t _simulatorPriority; - QUuid _simulatorID; // id of Node which is currently responsible for simulating this Entity - quint64 _simulationOwnershipExpiry; // time in future when ownership is back up for grabs + SimulationOwner _simulationOwner; QString _marketplaceID; QString _name; QString _href; //Hyperlink href diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 8d9b703874..9979caa9df 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -17,6 +17,7 @@ #include #include #include +#include // adebug #include "EntitiesLogging.h" #include "EntityItem.h" @@ -38,7 +39,7 @@ EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - EntityItemProperties::EntityItemProperties() : CONSTRUCT_PROPERTY(visible, ENTITY_ITEM_DEFAULT_VISIBLE), -CONSTRUCT_PROPERTY(position, 0), +CONSTRUCT_PROPERTY(position, 0.0f), CONSTRUCT_PROPERTY(dimensions, ENTITY_ITEM_DEFAULT_DIMENSIONS), CONSTRUCT_PROPERTY(rotation, ENTITY_ITEM_DEFAULT_ROTATION), CONSTRUCT_PROPERTY(density, ENTITY_ITEM_DEFAULT_DENSITY), @@ -73,8 +74,7 @@ CONSTRUCT_PROPERTY(locked, ENTITY_ITEM_DEFAULT_LOCKED), CONSTRUCT_PROPERTY(textures, ""), CONSTRUCT_PROPERTY(animationSettings, ""), CONSTRUCT_PROPERTY(userData, ENTITY_ITEM_DEFAULT_USER_DATA), -CONSTRUCT_PROPERTY(simulatorPriority, 0), -CONSTRUCT_PROPERTY(simulatorID, ENTITY_ITEM_DEFAULT_SIMULATOR_ID), +CONSTRUCT_PROPERTY(simulationOwner, SimulationOwner()), CONSTRUCT_PROPERTY(text, TextEntityItem::DEFAULT_TEXT), CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT), CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR), @@ -290,8 +290,8 @@ void EntityItemProperties::setBackgroundModeFromString(const QString& background EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags changedProperties; - CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions); CHECK_PROPERTY_CHANGE(PROP_POSITION, position); + CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions); CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation); CHECK_PROPERTY_CHANGE(PROP_DENSITY, density); CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity); @@ -325,9 +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_SIMULATION_OWNER, simulationOwner); CHECK_PROPERTY_CHANGE(PROP_TEXT, text); CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight); CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor); @@ -422,8 +420,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(locked); COPY_PROPERTY_TO_QSCRIPTVALUE(textures); COPY_PROPERTY_TO_QSCRIPTVALUE(userData); - COPY_PROPERTY_TO_QSCRIPTVALUE(simulatorPriority); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(simulatorID, getSimulatorIDAsString()); + //COPY_PROPERTY_TO_QSCRIPTVALUE(simulationOwner); // TODO: expose this for JSON saves? COPY_PROPERTY_TO_QSCRIPTVALUE(text); COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(textColor, getTextColor()); @@ -574,8 +571,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool auto result = QDateTime::fromMSecsSinceEpoch(_created / 1000, Qt::UTC); // usec per msec return result; }); - COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorPriority, float, setSimulatorPriority); - COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorID, QUuid, setSimulatorID); + // TODO: expose this to QScriptValue for JSON saves? + //COPY_PROPERTY_FROM_QSCRIPTVALUE(simulationOwner, ???, setSimulatorPriority); } _stage.copyFromScriptValue(object, _defaultSettings); @@ -710,41 +707,26 @@ 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(); + // adebug TODO: convert this to use APPEND_ENTITY_PROPERTY(P,V) macro? + if (requestedProperties.getHasProperty(PROP_SIMULATION_OWNER)) { LevelDetails propertyLevel = packetData->startLevel(); - if (packetData->appendRawData(ownershipData)) { - propertyFlags |= PROP_SIMULATOR_OWNERSHIP; + successPropertyFits = packetData->appendValue(properties._simulationOwner.toByteArray()); + if (successPropertyFits) { +// std::cout << "adebug appending ownerhip data" << std::endl; // adebug +// StreamUtil::dump(std::cout, properties._simulationOwner.toByteArray()); + propertyFlags |= PROP_SIMULATION_OWNER; + propertiesDidntFit -= PROP_SIMULATION_OWNER; propertyCount++; packetData->endLevel(propertyLevel); } else { - propertiesDidntFit -= PROP_SIMULATOR_OWNERSHIP; +// std::cout << "adebug ownership data did not fit" << std::endl; // adebug + packetData->discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; } + } else { +// std::cout << "adebug property doesn't have ownerhip data" << std::endl; // adebug + propertiesDidntFit -= PROP_SIMULATION_OWNER; } - */ APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition()); APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete @@ -768,66 +750,6 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, properties.getCollisionsWillMove()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked()); 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()); @@ -1059,7 +981,23 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int EntityPropertyFlags propertyFlags = encodedPropertyFlags; dataAt += propertyFlags.getEncodedLength(); processedBytes += propertyFlags.getEncodedLength(); - + + // adebug TODO: convert this to use READ_ENTITY_PROPERTY_TO_PROPERTIES macro? + if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { + QByteArray fromBuffer; + int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer); + + dataAt += bytes; + processedBytes += bytes; + SimulationOwner simOwner; + simOwner.fromByteArray(fromBuffer); + properties.setSimulationOwner(simOwner); +// std::cout << "adebug decoding ownerhip data" << std::endl; // adebug +// StreamUtil::dump(std::cout, fromBuffer); + } else { +// std::cout << "adebug no ownership info to decode" << std::endl; // adebug + } + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation); @@ -1082,8 +1020,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_PRIORITY, uint8_t, setSimulatorPriority); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_ID, QUuid, setSimulatorID); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); @@ -1199,6 +1135,7 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt } void EntityItemProperties::markAllChanged() { + _simulationOwnerChanged = true; _positionChanged = true; _dimensionsChanged = true; _rotationChanged = true; @@ -1211,8 +1148,6 @@ void EntityItemProperties::markAllChanged() { _frictionChanged = true; _lifetimeChanged = true; _userDataChanged = true; - _simulatorPriorityChanged = true; - _simulatorIDChanged = true; _scriptChanged = true; _scriptTimestampChanged = true; _collisionSoundURLChanged = true; @@ -1336,16 +1271,12 @@ bool EntityItemProperties::hasMiscPhysicsChanges() const { _compoundShapeURLChanged || _collisionsWillMoveChanged || _ignoreForCollisionsChanged; } -void EntityItemProperties::clearSimulatorOwnership() { - _simulatorID = QUuid(); - _simulatorPriority = 0; - _simulatorIDChanged = true; - _simulatorPriorityChanged = true; +void EntityItemProperties::clearSimulationOwner() { + _simulationOwner.clear(); + _simulationOwnerChanged = true; } -void EntityItemProperties::setSimulatorOwnership(const QUuid& id, uint8_t priority) { - _simulatorID = id; - _simulatorPriority = glm::max(priority, _simulatorPriority); - _simulatorIDChanged = true; - _simulatorPriorityChanged = true; +void EntityItemProperties::setSimulationOwner(const QUuid& id, uint8_t priority) { + _simulationOwner.set(id, priority); + _simulationOwnerChanged = true; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4210b342f2..f84e897331 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -34,6 +34,7 @@ #include "EntityItemPropertiesMacros.h" #include "EntityTypes.h" #include "EntityPropertyFlags.h" +#include "SimulationOwner.h" #include "SkyboxPropertyGroup.h" #include "StagePropertyGroup.h" @@ -120,8 +121,7 @@ public: DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString); DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(PROP_ANIMATION_SETTINGS, AnimationSettings, animationSettings, QString); DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString); - DEFINE_PROPERTY(PROP_SIMULATOR_PRIORITY, SimulatorPriority, simulatorPriority, uint8_t); - DEFINE_PROPERTY_REF(PROP_SIMULATOR_ID, SimulatorID, simulatorID, QUuid); + DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner); DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString); DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float); DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor); @@ -197,7 +197,7 @@ public: const QStringList& getTextureNames() const { return _textureNames; } void setTextureNames(const QStringList& value) { _textureNames = value; } - QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); } + QString getSimulatorIDAsString() const { return _simulationOwner.getID().toString().mid(1,36).toUpper(); } void setVoxelDataDirty() { _voxelDataChanged = true; } @@ -208,8 +208,8 @@ public: bool hasTerseUpdateChanges() const; bool hasMiscPhysicsChanges() const; - void clearSimulatorOwnership(); - void setSimulatorOwnership(const QUuid& id, uint8_t priority); + void clearSimulationOwner(); + void setSimulationOwner(const QUuid& id, uint8_t priority); private: QUuid _id; @@ -289,8 +289,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulatorPriority, simulatorPriority, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulatorID, simulatorID, QUuid()); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulationOwner, simulationOwner, SimulationOwner()); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 08569adaf4..163870c225 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -104,7 +104,7 @@ enum EntityPropertyList { PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities PROP_MARKETPLACE_ID, // all entities PROP_ACCELERATION, // all entities - PROP_SIMULATOR_ID, // all entities + PROP_SIMULATOR_ID, // unused PROP_NAME, // all entities PROP_COLLISION_SOUND_URL, PROP_RESTITUTION, @@ -121,7 +121,7 @@ enum EntityPropertyList { // used by hyperlinks PROP_HREF, PROP_DESCRIPTION, - PROP_SIMULATOR_PRIORITY, + PROP_SIMULATION_OWNER, PROP_FACE_CAMERA, PROP_SCRIPT_TIMESTAMP, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 6786763a83..0693c1e85c 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -9,18 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include // adebug +#include "EntityScriptingInterface.h" + #include +#include "EntitiesLogging.h" +#include "EntityActionFactoryInterface.h" +#include "EntityActionInterface.h" +#include "EntitySimulation.h" #include "EntityTree.h" #include "LightEntityItem.h" #include "ModelEntityItem.h" +#include "SimulationOwner.h" #include "ZoneEntityItem.h" -#include "EntitiesLogging.h" -#include "EntitySimulation.h" -#include "EntityActionInterface.h" -#include "EntityActionFactoryInterface.h" -#include "EntityScriptingInterface.h" EntityScriptingInterface::EntityScriptingInterface() : _entityTree(NULL) @@ -76,10 +79,10 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties // This Node is creating a new object. If it's in motion, set this Node as the simulator. auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); - propertiesWithSimID.setSimulatorOwnership(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY); + propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY); // and make note of it now, so we can act on it right away. - entity->setSimulatorID(propertiesWithSimID.getSimulatorID()); + entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY); entity->setLastBroadcast(usecTimestampNow()); } else { @@ -160,11 +163,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties proper // simulation. // we re-assert our simulation ownership - properties.setSimulatorOwnership(myNodeID, + properties.setSimulationOwner(myNodeID, glm::max(entity->getSimulatorPriority(), SCRIPT_EDIT_SIMULATOR_PRIORITY)); } else { // we make a bid for simulation ownership - properties.setSimulatorOwnership(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY); + properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATOR_PRIORITY); entity->flagForOwnership(); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 4a03b0a6df..45b5a95271 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -150,14 +150,14 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI } else { if (getIsServer()) { bool simulationBlocked = !entity->getSimulatorID().isNull(); - if (properties.simulatorIDChanged()) { - QUuid submittedID = properties.getSimulatorID(); + if (properties.simulationOwnerChanged()) { + QUuid submittedID = properties.getSimulationOwner().getID(); // a legit interface will only submit their own ID or NULL: if (submittedID.isNull()) { if (entity->getSimulatorID() == senderID) { // We only allow the simulation owner to clear their own simulationID's. simulationBlocked = false; - properties.setSimulatorPriority(0); // clear priority irregardless of priority sent + properties.clearSimulationOwner(); // clear everything } // else: We assume the sender really did believe it was the simulation owner when it sent } else if (submittedID == senderID) { @@ -170,10 +170,9 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI // (1) higher priority wins // (2) equal priority wins if ownership filter has expired except... uint8_t oldPriority = entity->getSimulatorPriority(); - uint8_t newPriority = properties.getSimulatorPriority(); + uint8_t newPriority = properties.getSimulationOwner().getPriority(); if (newPriority > oldPriority || - (newPriority == oldPriority && - usecTimestampNow() > entity->getSimulationOwnershipExpiry())) { + (newPriority == oldPriority && properties.getSimulationOwner().hasExpired())) { simulationBlocked = false; } } @@ -183,11 +182,13 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI } } if (simulationBlocked) { - // squash the physics-related changes. - properties.setSimulatorPriorityChanged(false); - properties.setSimulatorIDChanged(false); + // squash ownership and physics-related changes. + properties.setSimulationOwnerChanged(false); properties.setPositionChanged(false); properties.setRotationChanged(false); + properties.setVelocityChanged(false); + properties.setAngularVelocityChanged(false); + properties.setAccelerationChanged(false); } } // else client accepts what the server says @@ -203,7 +204,6 @@ 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/entities/src/SimulationOwner.cpp b/libraries/entities/src/SimulationOwner.cpp new file mode 100644 index 0000000000..f858665260 --- /dev/null +++ b/libraries/entities/src/SimulationOwner.cpp @@ -0,0 +1,188 @@ +// +// SimulationOwner.cpp +// libraries/entities/src +// +// Created by Andrew Meadows on 2015.06.19 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "SimulationOwner.h" + +#include + +#include + +// static +const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1; + + +SimulationOwner::SimulationOwner(const SimulationOwner& other) + : _id(other._id), _priority(other._priority), _expiry(other._expiry) { +} + +QByteArray SimulationOwner::toByteArray() const { + QByteArray data = _id.toRfc4122(); + data.append(_priority); + return data; +} + +bool SimulationOwner::fromByteArray(const QByteArray& data) { + if (data.size() == NUM_BYTES_ENCODED) { + QByteArray idBytes = data.left(NUM_BYTES_RFC4122_UUID); + _id = QUuid::fromRfc4122(idBytes); + _priority = data[NUM_BYTES_RFC4122_UUID]; + return true; + } + return false; +} + +void SimulationOwner::clear() { + _id = QUuid(); + _priority = 0; + _expiry = 0; +} + +void SimulationOwner::setPriority(quint8 priority) { + _priority = priority; + if (_priority == MAX_SIMULATOR_PRIORITY) { + // we extend the the expiry whenever we set MAX_SIMULATOR_PRIORITY + updateExpiry(); + } else if (_priority == 0) { + // when priority is zero we clear everything + _expiry = 0; + _id = QUuid(); + } +} + +bool SimulationOwner::setID(const QUuid& id) { + if (_id != id) { + _id = id; + updateExpiry(); + if (_id.isNull()) { + // when id is null we clear everything + _priority = 0; + _expiry = 0; + } + return true; + } + return false; +} + +bool SimulationOwner::set(const QUuid& id, quint8 priority) { + setPriority(priority); + return setID(id); +} + +bool SimulationOwner::set(const SimulationOwner& owner) { + setPriority(owner._priority); + return setID(owner._id); +} + +void SimulationOwner::updateExpiry() { + const quint64 OWNERSHIP_LOCKOUT_EXPIRY = USECS_PER_SECOND / 5; + _expiry = usecTimestampNow() + OWNERSHIP_LOCKOUT_EXPIRY; +} + +// TODO: move this test code out +// static debug +void SimulationOwner::test() { + { // test default constructor + SimulationOwner simOwner; + if (!simOwner.isNull()) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner should be NULL" << std::endl; + } + + if (simOwner.getPriority() != 0) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : unexpeced SimulationOwner priority" << std::endl; + } + } + + { // test set constructor + QUuid id = QUuid::createUuid(); + quint8 priority = 128; + SimulationOwner simOwner(id, priority); + if (simOwner.isNull()) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner should NOT be NULL" << std::endl; + } + + if (simOwner.getID() != id) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner with unexpected id" << std::endl; + } + + if (simOwner.getPriority() != priority) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : unexpeced SimulationOwner priority" << std::endl; + } + + QUuid otherID = QUuid::createUuid(); + if (simOwner.getID() == otherID) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner with unexpected id" << std::endl; + } + } + + { // test set() + QUuid id = QUuid::createUuid(); + quint8 priority = 1; + SimulationOwner simOwner; + simOwner.set(id, priority); + if (simOwner.isNull()) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner should NOT be NULL" << std::endl; + } + + if (simOwner.getID() != id) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : SimulationOwner with unexpected id" << std::endl; + } + + if (simOwner.getPriority() != priority) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : unexpeced SimulationOwner priority" << std::endl; + } + } + + { // test encode/decode + SimulationOwner ownerA(QUuid::createUuid(), 1); + SimulationOwner ownerB(QUuid::createUuid(), 2); + + QByteArray data = ownerA.toByteArray(); + ownerB.fromByteArray(data); + + if (ownerA.getID() != ownerB.getID()) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : ownerA._id should be equal to ownerB._id" << std::endl; + } + } +} + +bool SimulationOwner::operator==(const SimulationOwner& other) { + return (_id == other._id && _priority == other._priority); +} + +bool SimulationOwner::operator!=(const SimulationOwner& other) { + return (_id != other._id && _priority != other._priority); +} + +SimulationOwner& SimulationOwner::operator=(const SimulationOwner& other) { + _priority = other._priority; + if (_priority == 0) { + _id = QUuid(); + _expiry = 0; + } else { + if (_id != other._id) { + updateExpiry(); + } + _id = other._id; + } + return *this; +} + +// friend of SimulationOwner +std::ostream& operator<<(std::ostream& s, const SimulationOwner& simOwner) { + s << "{ id : " << simOwner._id.toString().toStdString() << ", priority : " << (int)simOwner._priority << " }"; + return s; +} + +QDebug& operator<<(QDebug& d, const SimulationOwner& simOwner) { + d << "{ id : " << simOwner << ", priority : " << (int)simOwner._priority << " }"; + return d; +} + diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h new file mode 100644 index 0000000000..449bf85f7a --- /dev/null +++ b/libraries/entities/src/SimulationOwner.h @@ -0,0 +1,78 @@ +// +// SimulationOwner.h +// libraries/entities/src +// +// Created by Andrew Meadows on 2015.06.19 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_SimulationOwner_h +#define hifi_SimulationOwner_h + +#include + +#include +#include + +#include +#include + +const quint8 VOLUNTEER_SIMULATOR_PRIORITY = 0x01; +const quint8 SCRIPT_EDIT_SIMULATOR_PRIORITY = 0x80; +const quint8 MAX_SIMULATOR_PRIORITY = 0xff; +const quint8 ATTACHMENT_SIMULATOR_PRIORITY = MAX_SIMULATOR_PRIORITY; + +class SimulationOwner { +public: + static const int NUM_BYTES_ENCODED; + + SimulationOwner() : _id(), _priority(0), _expiry(0) {} + SimulationOwner(const QUuid& id, quint8 priority) : _id(id), _priority(priority), _expiry(0) {} + SimulationOwner(const SimulationOwner& other); + + const QUuid& getID() const { return _id; } + quint8 getPriority() const { return _priority; } + const quint64& getExpiry() const { return _expiry; } + + QByteArray toByteArray() const; + bool fromByteArray(const QByteArray& data); + + void clear(); + + void setPriority(quint8 priority); + + // return true if id is changed + bool setID(const QUuid& id); + bool set(const QUuid& id, quint8 priority); + bool set(const SimulationOwner& owner); + + bool isNull() const { return _id.isNull(); } + bool matchesID(const QUuid& id) const { return _id == id && !_id.isNull(); } + + void updateExpiry(); + + bool hasExpired() const { return usecTimestampNow() > _expiry; } + + bool operator>=(quint8 priority) const { return _priority >= priority; } + bool operator==(const SimulationOwner& other); + bool operator!=(const SimulationOwner& other); + SimulationOwner& operator=(const SimulationOwner& other); + + friend std::ostream& operator<<(std::ostream& s, const SimulationOwner& simOwner); + friend QDebug& operator<<(QDebug& d, const SimulationOwner& simOwner); + + // debug + static void test(); + +private: + QUuid _id; + quint8 _priority; + quint64 _expiry; +}; + + + +#endif // hifi_SimulationOwner_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 1c14c3dbde..1a579cb6c9 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -73,7 +73,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketTypeEntityAdd: case PacketTypeEntityEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_HAVE_SIMULATOR_PRIORITY; + return VERSION_ENTITIES_HAVE_SIMULATION_OWNER; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 1dc66b23ca..20d3db72c8 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -249,6 +249,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); + QByteArray getRawData() const { return QByteArray((const char*)_uncompressed, _bytesInUse); } // adebug private: /// appends raw bytes, might fail if byte would cause packet to be too large diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index f2f5991124..64b0c317bd 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -22,7 +22,8 @@ #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS #include "EntityTree.h" #endif -const char* plankyBlock = "PlankyBlock46"; // adebug +//const char* plankyBlock = "PlankyBlock46"; // adebug +const char* plankyBlock = "magenta"; // adebug static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; @@ -97,17 +98,17 @@ void EntityMotionState::updateServerPhysicsVariables() { // virtual 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 - } +// 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, engine); if (flags & EntityItem::DIRTY_SIMULATOR_ID) { - if (_entity && _entity->getName() == plankyBlock) { - std::cout << "adebug handleEasyChanges() '" << _entity->getName().toStdString() << "' found DIRTY_SIMULATOR_ID flag" << std::endl; // adebug - } +// if (_entity && _entity->getName() == plankyBlock) { +// std::cout << "adebug handleEasyChanges() '" << _entity->getName().toStdString() << "' found DIRTY_SIMULATOR_ID flag" << std::endl; // adebug +// } _loopsWithoutOwner = 0; if (_entity->getSimulatorID().isNull()) { // simulation ownership is being removed @@ -116,9 +117,9 @@ void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) flags &= ~EntityItem::DIRTY_PHYSICS_ACTIVATION; // hint to Bullet that the object is deactivating _body->setActivationState(WANTS_DEACTIVATION); - if (_entity && _entity->getName() == plankyBlock) { - std::cout << "adebug handleEasyChanges() '" << _entity->getName().toStdString() << "' clearing ownership so _candidatePriority goes to 0" << std::endl; // adebug - } +// 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 @@ -128,9 +129,9 @@ void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) _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 (_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 } @@ -478,14 +479,14 @@ 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 // 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(); + properties.clearSimulationOwner(); 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()); + properties.setSimulationOwner(sessionID, _entity->getSimulatorPriority()); _expectedOwnership = 0; } } else { @@ -495,7 +496,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q 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)); + properties.setSimulationOwner(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