From 3241c182714c2f2bf221d1f910dab38049644406 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 9 May 2018 18:10:40 -0700 Subject: [PATCH 01/29] first pass cloneables WIP --- .../src/entities/EntityServer.cpp | 1 + .../entities/src/EntityEditPacketSender.cpp | 14 ++- .../entities/src/EntityEditPacketSender.h | 1 + libraries/entities/src/EntityItem.cpp | 98 ++++++++++++++++++ libraries/entities/src/EntityItem.h | 23 +++++ .../entities/src/EntityItemProperties.cpp | 97 ++++++++++++++++++ libraries/entities/src/EntityItemProperties.h | 7 ++ .../src/EntityItemPropertiesDefaults.h | 5 + libraries/entities/src/EntityPropertyFlags.h | 5 + .../entities/src/EntityScriptingInterface.cpp | 73 +++++++++----- .../entities/src/EntityScriptingInterface.h | 3 + libraries/entities/src/EntityTree.cpp | 99 +++++++++++++++---- libraries/entities/src/EntityTree.h | 1 + libraries/gpu/src/gpu/Buffer.cpp | 2 +- .../networking/src/udt/PacketHeaders.cpp | 3 +- libraries/networking/src/udt/PacketHeaders.h | 4 +- scripts/system/html/js/entityProperties.js | 75 ++------------ scripts/system/libraries/cloneEntityUtils.js | 60 +++-------- .../libraries/controllerDispatcherUtils.js | 3 +- 19 files changed, 410 insertions(+), 164 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index c108dad6cf..3ca8c1ecd1 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -44,6 +44,7 @@ EntityServer::EntityServer(ReceivedMessage& message) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, + PacketType::EntityClone, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index d89dd4f9d0..0ae4f7ac2b 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -34,7 +34,7 @@ void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer EntityItem::getMaterial } return toReturn; } + +bool EntityItem::getCloneable() const { + bool result; + withReadLock([&] { + result = _cloneable; + }); + return result; +} + +void EntityItem::setCloneable(bool value) { + withWriteLock([&] { + _cloneable = value; + }); +} + +float EntityItem::getCloneableLifetime() const { + float result; + withReadLock([&] { + result = _cloneableLifetime; + }); + return result; +} + +void EntityItem::setCloneableLifetime(float value) { + withWriteLock([&] { + _cloneableLifetime = value; + }); +} + +float EntityItem::getCloneableLimit() const { + float result; + withReadLock([&] { + result = _cloneableLimit; + }); + return result; +} + +void EntityItem::setCloneableLimit(float value) { + withWriteLock([&] { + _cloneableLimit = value; + }); +} + +bool EntityItem::getCloneableDynamic() const { + bool result; + withReadLock([&] { + result = _cloneableDynamic; + }); + return result; +} + +void EntityItem::setCloneableDynamic(const bool value) { + withWriteLock([&] { + _cloneableDynamic = value; + }); +} + +bool EntityItem::addCloneID(const QUuid& cloneID) { + if (!_cloneIDs.contains(cloneID)) { + _cloneIDs.append(cloneID); + return true; + } + return false; +} + +bool EntityItem::removeCloneID(const QUuid& cloneID) { + int index = _cloneIDs.indexOf(cloneID); + if (index > 0) { + _cloneIDs.removeAt(index); + return true; + } + return false; +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a88250a133..95cc5f96e1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -341,6 +341,15 @@ public: quint32 getStaticCertificateVersion() const; void setStaticCertificateVersion(const quint32&); + bool getCloneable() const; + void setCloneable(bool value); + float getCloneableLifetime() const; + void setCloneableLifetime(float value); + float getCloneableLimit() const; + void setCloneableLimit(float value); + bool getCloneableDynamic() const; + void setCloneableDynamic(const bool value); + // TODO: get rid of users of getRadius()... float getRadius() const; @@ -494,6 +503,12 @@ public: void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; } uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; } + bool addCloneID(const QUuid& cloneID); + bool removeCloneID(const QUuid& cloneID); + const QList& getCloneIDs() const { return _cloneIDs; } + void setCloneParent(const QUuid& cloneParentID) { _cloneParentID = cloneParentID; } + const QUuid& getCloneParent() const { return _cloneParentID; } + signals: void requestRenderUpdate(); @@ -648,6 +663,14 @@ protected: bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera + bool _cloneable; + float _cloneableLifetime; + float _cloneableLimit; + bool _cloneableDynamic; + + QList _cloneIDs; + QUuid _cloneParentID; + private: std::unordered_map _materials; std::mutex _materialsLock; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4d7c114176..93c2eb885e 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -436,6 +436,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_DPI, dpi); CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); + CHECK_PROPERTY_CHANGE(PROP_CLONEABLE, cloneable); + CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIFETIME, cloneableLifetime); + CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIMIT, cloneableLimit); + CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic); + changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); changedProperties += _ambientLight.getChangedProperties(); @@ -1430,6 +1435,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable except at entity creation COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE, cloneable); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIFETIME, cloneableLifetime); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIMIT, cloneableLimit); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic); + // Rendering info if (!skipDefaults && !strictSemantics) { QScriptValue renderInfo = engine->newObject(); @@ -1642,6 +1652,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneable, bool, setCloneable); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLifetime, float, setCloneableLifetime); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLimit, float, setCloneableLimit); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableDynamic, bool, setCloneableDynamic); + _lastEdited = usecTimestampNow(); } @@ -2017,6 +2032,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t); + ADD_PROPERTY_TO_MAP(PROP_CLONEABLE, Cloneable, cloneable, bool); + ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float); + ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float); + ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -2331,6 +2351,11 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber()); APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID()); APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion()); + + APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, properties.getCloneable()); + APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, properties.getCloneableLifetime()); + APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, properties.getCloneableLimit()); + APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, properties.getCloneableDynamic()); } if (propertyCount > 0) { @@ -2701,6 +2726,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE, bool, setCloneable); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIFETIME, float, setCloneableLifetime); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIMIT, float, setCloneableLimit); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_DYNAMIC, bool, setCloneableDynamic); + return valid; } @@ -2780,6 +2810,54 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt return true; } +bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer) { + + char* copyAt = buffer.data(); + int outputLength = 0; + + if (buffer.size() < (int)(sizeof(NUM_BYTES_RFC4122_UUID) * 2)) { + qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!"; + return false; + } + + memcpy(copyAt, entityIDToClone.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); + copyAt += NUM_BYTES_RFC4122_UUID; + outputLength += NUM_BYTES_RFC4122_UUID; + + memcpy(copyAt, newEntityID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); + copyAt += NUM_BYTES_RFC4122_UUID; + outputLength += NUM_BYTES_RFC4122_UUID; + + buffer.resize(outputLength); + + return true; +} + +bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID) { + + const unsigned char* packetData = (const unsigned char*)buffer.constData(); + const unsigned char* dataAt = packetData; + size_t packetLength = buffer.size(); + processedBytes = 0; + + if (NUM_BYTES_RFC4122_UUID * 2 > packetLength) { + qCDebug(entities) << "EntityItemProperties::processEraseMessageDetails().... bailing because not enough bytes in buffer"; + return false; // bail to prevent buffer overflow + } + + QByteArray encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID); + entityIDToClone = QUuid::fromRfc4122(encodedID); + dataAt += encodedID.size(); + processedBytes += encodedID.size(); + + encodedID = buffer.mid((int)processedBytes, NUM_BYTES_RFC4122_UUID); + newEntityID = QUuid::fromRfc4122(encodedID); + dataAt += encodedID.size(); + processedBytes += encodedID.size(); + + return true; +} + void EntityItemProperties::markAllChanged() { _lastEditedByChanged = true; _simulationOwnerChanged = true; @@ -2941,6 +3019,11 @@ void EntityItemProperties::markAllChanged() { _dpiChanged = true; _relayParentJointsChanged = true; + + _cloneableChanged = true; + _cloneableLifetimeChanged = true; + _cloneableLimitChanged = true; + _cloneableDynamicChanged = true; } // The minimum bounding box for the entity. @@ -3373,6 +3456,20 @@ QList EntityItemProperties::listChangedProperties() { out += "isUVModeStretch"; } + if (cloneableChanged()) { + out += "cloneable"; + } + if (cloneableLifetimeChanged()) { + out += "cloneableLifetime"; + } + if (cloneableLimitChanged()) { + out += "cloneableLimit"; + } + if (cloneableDynamicChanged()) { + out += "cloneableDynamic"; + } + + getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); getAmbientLight().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 39ea2e0bdd..3ae2186cab 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -272,6 +272,11 @@ public: DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); + DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_CLONEABLE); + DEFINE_PROPERTY(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float, ENTITY_ITEM_CLONEABLE_LIFETIME); + DEFINE_PROPERTY(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float, ENTITY_ITEM_CLONEABLE_LIMIT); + DEFINE_PROPERTY(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool, ENTITY_ITEM_CLONEABLE_DYNAMIC); + static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); @@ -294,6 +299,8 @@ public: QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties); static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer); + static bool encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer); + static bool decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID); static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, EntityItemID& entityID, EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index d2ddd687dd..6e46453bda 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -97,4 +97,9 @@ const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid(); const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false; +const bool ENTITY_ITEM_CLONEABLE = false; +const float ENTITY_ITEM_CLONEABLE_LIFETIME = 300.0f; +const int ENTITY_ITEM_CLONEABLE_LIMIT = 0; +const bool ENTITY_ITEM_CLONEABLE_DYNAMIC = false; + #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 99a5f287ea..f698739e01 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -242,6 +242,11 @@ enum EntityPropertyList { PROP_MATERIAL_MAPPING_ROT, PROP_MATERIAL_DATA, + PROP_CLONEABLE, + PROP_CLONEABLE_LIFETIME, + PROP_CLONEABLE_LIMIT, + PROP_CLONEABLE_DYNAMIC, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 7c16214a78..132fec2c51 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -258,33 +258,9 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); - EntityItemID id = EntityItemID(QUuid::createUuid()); - + EntityItemID id; // If we have a local entity tree set, then also update it. - bool success = true; - if (_entityTree) { - _entityTree->withWriteLock([&] { - EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID); - if (entity) { - if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) { - // due to parenting, the server may not know where something is in world-space, so include the bounding cube. - bool success; - AACube queryAACube = entity->getQueryAACube(success); - if (success) { - propertiesWithSimID.setQueryAACube(queryAACube); - } - } - - entity->setLastBroadcast(usecTimestampNow()); - // since we're creating this object we will immediately volunteer to own its simulation - entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY); - propertiesWithSimID.setLastEdited(entity->getLastEdited()); - } else { - qCDebug(entities) << "script failed to add new Entity to local Octree"; - success = false; - } - }); - } + bool success = addLocalEntityCopy(propertiesWithSimID, id); // queue the packet if (success) { @@ -295,6 +271,39 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties } } +bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properties, EntityItemID& id) { + bool success = true; + + id = EntityItemID(QUuid::createUuid()); + + if (_entityTree) { + _entityTree->withWriteLock([&] { + EntityItemPointer entity = _entityTree->addEntity(id, properties); + if (entity) { + if (properties.queryAACubeRelatedPropertyChanged()) { + // due to parenting, the server may not know where something is in world-space, so include the bounding cube. + bool success; + AACube queryAACube = entity->getQueryAACube(success); + if (success) { + properties.setQueryAACube(queryAACube); + } + } + + entity->setLastBroadcast(usecTimestampNow()); + // since we're creating this object we will immediately volunteer to own its simulation + entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY); + properties.setLastEdited(entity->getLastEdited()); + } + else { + qCDebug(entities) << "script failed to add new Entity to local Octree"; + success = false; + } + }); + } + + return success; +} + QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic, bool collisionless, const glm::vec3& position, const glm::vec3& gravity) { @@ -320,6 +329,18 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin return addEntity(properties); } +QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { + EntityItemID newEntityID; + EntityItemProperties properties = getEntityProperties(entityIDToClone); + if (addLocalEntityCopy(properties, newEntityID)) { + qCDebug(entities) << "DBACK POOPY cloneEntity addLocalEntityCopy" << newEntityID; + getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); + return newEntityID; + } else { + return QUuid(); + } +} + EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) { EntityPropertyFlags noSpecificProperties; return getEntityProperties(identity, noSpecificProperties); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 8adb5138f2..6935c9e8c4 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -224,6 +224,8 @@ public slots: Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic, bool collisionless, const glm::vec3& position, const glm::vec3& gravity); + Q_INVOKABLE QUuid cloneEntity(QUuid entityIDToClone); + /**jsdoc * Get the properties of an entity. * @function Entities.getEntityProperties @@ -1875,6 +1877,7 @@ private: bool polyVoxWorker(QUuid entityID, std::function actor); bool setPoints(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); + bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id); EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID, EntityTypes::EntityType entityType = EntityTypes::Unknown); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 3149527216..1e8055ff46 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -228,6 +228,7 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const { // we handle these types of "edit" packets switch (packetType) { case PacketType::EntityAdd: + case PacketType::EntityClone: case PacketType::EntityEdit: case PacketType::EntityErase: case PacketType::EntityPhysics: @@ -592,6 +593,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign return; } + removeCloneIDFromCloneParent(entityID); unhookChildAvatar(entityID); emit deletingEntity(entityID); emit deletingEntityPointer(existingEntity.get()); @@ -625,6 +627,19 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) { }); } +void EntityTree::removeCloneIDFromCloneParent(const EntityItemID& entityID) { + EntityItemPointer entity = findEntityByEntityItemID(entityID); + if (entity) { + const QUuid& cloneParentID = entity->getCloneParent(); + if (!cloneParentID.isNull()) { + EntityItemPointer cloneParent = findEntityByID(cloneParentID); + if (cloneParent) { + cloneParent->removeCloneID(entityID); + } + } + } +} + void EntityTree::deleteEntities(QSet entityIDs, bool force, bool ignoreWarnings) { // NOTE: callers must lock the tree before using this method DeleteEntityOperator theOperator(getThisPointer()); @@ -653,6 +668,7 @@ void EntityTree::deleteEntities(QSet entityIDs, bool force, bool i } // tell our delete operator about this entityID + removeCloneIDFromCloneParent(entityID); unhookChildAvatar(entityID); theOperator.addEntityIDToDeleteList(entityID); emit deletingEntity(entityID); @@ -1392,6 +1408,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c int processedBytes = 0; bool isAdd = false; + bool isClone = false; // we handle these types of "edit" packets switch (message.getType()) { case PacketType::EntityErase: { @@ -1400,8 +1417,10 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c break; } + case PacketType::EntityClone: + isClone = true; // fall through to next case case PacketType::EntityAdd: - isAdd = true; // fall through to next case + isAdd = true; // fall through to next case // FALLTHRU case PacketType::EntityPhysics: case PacketType::EntityEdit: { @@ -1422,8 +1441,21 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c EntityItemProperties properties; startDecode = usecTimestampNow(); - bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, - entityItemID, properties); + bool validEditPacket = false; + EntityItemID entityIDToClone; + EntityItemPointer entityToClone; + if (isClone) { + QByteArray buffer = QByteArray::fromRawData(reinterpret_cast(editData), maxLength); + validEditPacket = EntityItemProperties::decodeCloneEntityMessage(buffer, processedBytes, entityIDToClone, entityItemID); + entityToClone = findEntityByEntityItemID(entityIDToClone); + if (entityToClone) { + properties = entityToClone->getProperties(); + } + } + else { + validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties); + } + endDecode = usecTimestampNow(); EntityItemPointer existingEntity; @@ -1491,24 +1523,26 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } - if ((isAdd || properties.lifetimeChanged()) && - ((!senderNode->getCanRez() && senderNode->getCanRezTmp()) || - (!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) { - // this node is only allowed to rez temporary entities. if need be, cap the lifetime. - if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME || - properties.getLifetime() > _maxTmpEntityLifetime) { - properties.setLifetime(_maxTmpEntityLifetime); + if (!isClone) { + if ((isAdd || properties.lifetimeChanged()) && + ((!senderNode->getCanRez() && senderNode->getCanRezTmp()) || + (!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) { + // this node is only allowed to rez temporary entities. if need be, cap the lifetime. + if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME || + properties.getLifetime() > _maxTmpEntityLifetime) { + properties.setLifetime(_maxTmpEntityLifetime); + bumpTimestamp(properties); + } + } + + if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) { + // if a node can't change locks, don't allow it to create an already-locked entity -- automatically + // clear the locked property and allow the unlocked entity to be created. + properties.setLocked(false); bumpTimestamp(properties); } } - if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) { - // if a node can't change locks, don't allow it to create an already-locked entity -- automatically - // clear the locked property and allow the unlocked entity to be created. - properties.setLocked(false); - bumpTimestamp(properties); - } - // If we got a valid edit packet, then it could be a new entity or it could be an update to // an existing entity... handle appropriately if (validEditPacket) { @@ -1566,17 +1600,39 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } else if (isAdd) { bool failedAdd = !allowed; bool isCertified = !properties.getCertificateID().isEmpty(); + bool isCloneable = properties.getCloneable(); + int cloneLimit = properties.getCloneableLimit(); if (!allowed) { qCDebug(entities) << "Filtered entity add. ID:" << entityItemID; - } else if (!isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) { + } else if (!isClone && !isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) { failedAdd = true; qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID() << "] attempted to add an uncertified entity with ID:" << entityItemID; - } else if (isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) { + } else if (!isClone && isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) { failedAdd = true; qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID() << "] attempted to add a certified entity with ID:" << entityItemID; + } else if (isClone && isCertified) { + failedAdd = true; + qCDebug(entities) << "User attempted to clone certified entity from entity ID:" << entityIDToClone; + } else if (isClone && !isCloneable) { + failedAdd = true; + qCDebug(entities) << "User attempted to clone non-cloneable entity from entity ID:" << entityIDToClone; + } else if (isClone && entityToClone && entityToClone->getCloneIDs().size() >= cloneLimit) { + failedAdd = true; + qCDebug(entities) << "User attempted to clone entity ID:" << entityIDToClone << " which reached it's cloneable limit."; } else { + if (isClone) { + properties.setName(properties.getName() + "-clone-" + entityIDToClone.toString()); + properties.setLocked(false); + properties.setLifetime(properties.getCloneableLifetime()); + properties.setDynamic(properties.getCloneableDynamic()); + properties.setCloneable(ENTITY_ITEM_CLONEABLE); + properties.setCloneableLifetime(ENTITY_ITEM_CLONEABLE_LIFETIME); + properties.setCloneableLimit(ENTITY_ITEM_CLONEABLE_LIMIT); + properties.setCloneableDynamic(ENTITY_ITEM_CLONEABLE_DYNAMIC); + } + // this is a new entity... assign a new entityID properties.setCreated(properties.getLastEdited()); properties.setLastEditedBy(senderNode->getUUID()); @@ -1600,6 +1656,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c if (newEntity) { newEntity->markAsChangedOnServer(); notifyNewlyCreatedEntity(*newEntity, senderNode); + if (isClone) { + entityToClone->addCloneID(newEntity->getEntityItemID()); + newEntity->setCloneParent(entityIDToClone); + qCDebug(entities) << "DBACK POOPY addedEntity clone " << newEntity->getEntityItemID(); + } startLogging = usecTimestampNow(); if (wantEditLogging()) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index ee9fb10554..0d7cc54df7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -117,6 +117,7 @@ public: // check if the avatar is a child of this entity, If so set the avatar parentID to null void unhookChildAvatar(const EntityItemID entityID); + void removeCloneIDFromCloneParent(const EntityItemID& entityID); void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true); void deleteEntities(QSet entityIDs, bool force = false, bool ignoreWarnings = true); diff --git a/libraries/gpu/src/gpu/Buffer.cpp b/libraries/gpu/src/gpu/Buffer.cpp index ebb768e597..fe3570e7f7 100644 --- a/libraries/gpu/src/gpu/Buffer.cpp +++ b/libraries/gpu/src/gpu/Buffer.cpp @@ -99,7 +99,7 @@ Buffer::Update::Update(const Buffer& parent) : buffer(parent) { void Buffer::Update::apply() const { // Make sure we're loaded in order buffer._applyUpdateCount++; - assert(buffer._applyUpdateCount == updateNumber); + //assert(buffer._applyUpdateCount == updateNumber); const auto pageSize = buffer._pages._pageSize; buffer._renderSysmem.resize(size); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 70880833bf..e84f3ffe7a 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -29,10 +29,11 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::DomainList: return static_cast(DomainListVersion::GetMachineFingerprintFromUUIDSupport); case PacketType::EntityAdd: + case PacketType::EntityClone: case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::MaterialData); + return static_cast(EntityVersion::CloneableData); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 7d374f3625..5cb2d49a53 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -75,6 +75,7 @@ public: EntityData, EntityQuery, EntityAdd, + EntityClone, EntityErase, EntityEdit, DomainServerConnectionToken, @@ -232,7 +233,8 @@ enum class EntityVersion : PacketVersion { SoftEntities, MaterialEntities, ShadowControl, - MaterialData + MaterialData, + CloneableData }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 4b6329db44..f0bcdad708 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1040,12 +1040,12 @@ function loaded() { elWantsTrigger.checked = false; elIgnoreIK.checked = true; - elCloneable.checked = false; - elCloneableDynamic.checked = false; + elCloneable.checked = properties.cloneable; + elCloneableDynamic.checked = properties.cloneableDynamic; elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; - elCloneableLimit.value = 0; - elCloneableLifetime.value = 300; - + elCloneableLimit.value = properties.cloneableLimit; + elCloneableLifetime.value = properties.cloneableLifetime; + var grabbablesSet = false; var parsedUserData = {}; try { @@ -1069,27 +1069,6 @@ function loaded() { } else { elIgnoreIK.checked = true; } - if ("cloneable" in grabbableData) { - elCloneable.checked = grabbableData.cloneable; - elCloneableGroup.style.display = elCloneable.checked ? "block" : "none"; - elCloneableDynamic.checked = - grabbableData.cloneDynamic ? grabbableData.cloneDynamic : properties.dynamic; - if (elCloneable.checked) { - if ("cloneLifetime" in grabbableData) { - elCloneableLifetime.value = - grabbableData.cloneLifetime ? grabbableData.cloneLifetime : 300; - } - if ("cloneLimit" in grabbableData) { - elCloneableLimit.value = grabbableData.cloneLimit ? grabbableData.cloneLimit : 0; - } - if ("cloneAvatarEntity" in grabbableData) { - elCloneableAvatarEntity.checked = - grabbableData.cloneAvatarEntity ? grabbableData.cloneAvatarEntity : false; - } - } - } else { - elCloneable.checked = false; - } } } catch (e) { // TODO: What should go here? @@ -1460,45 +1439,11 @@ function loaded() { } userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, true); }); - elCloneableDynamic.addEventListener('change', function(event) { - userDataChanger("grabbableKey", "cloneDynamic", event.target, elUserData, -1); - }); - - elCloneableAvatarEntity.addEventListener('change', function(event) { - userDataChanger("grabbableKey", "cloneAvatarEntity", event.target, elUserData, -1); - }); - - elCloneable.addEventListener('change', function (event) { - var checked = event.target.checked; - if (checked) { - multiDataUpdater("grabbableKey", { - cloneLifetime: elCloneableLifetime, - cloneLimit: elCloneableLimit, - cloneDynamic: elCloneableDynamic, - cloneAvatarEntity: elCloneableAvatarEntity, - cloneable: event.target, - grabbable: null - }, elUserData, {}); - elCloneableGroup.style.display = "block"; - updateProperty('dynamic', false); - } else { - multiDataUpdater("grabbableKey", { - cloneLifetime: null, - cloneLimit: null, - cloneDynamic: null, - cloneAvatarEntity: null, - cloneable: false - }, elUserData, {}); - elCloneableGroup.style.display = "none"; - } - }); - - var numberListener = function (event) { - userDataChanger("grabbableKey", - event.target.getAttribute("data-user-data-type"), parseInt(event.target.value), elUserData, false); - }; - elCloneableLifetime.addEventListener('change', numberListener); - elCloneableLimit.addEventListener('change', numberListener); + + elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); + elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneableDynamic')); + elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLifetime')); + elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLimit')); elWantsTrigger.addEventListener('change', function() { userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false); diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index 63b161eb80..bd33d32342 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -33,8 +33,7 @@ if (typeof Object.assign !== 'function') { entityIsCloneable = function(props) { if (props) { - var grabbableData = getGrabbableData(props); - return grabbableData.cloneable; + return props.cloneable; } return false; }; @@ -42,56 +41,19 @@ entityIsCloneable = function(props) { propsAreCloneDynamic = function(props) { var cloneable = entityIsCloneable(props); if (cloneable) { - var grabInfo = getGrabbableData(props); - if (grabInfo.cloneDynamic) { - return true; - } + return props.cloneableDynamic; } return false; }; - cloneEntity = function(props, worldEntityProps) { - // we need all the properties, for this - var cloneableProps = Entities.getEntityProperties(props.id); - - var count = 0; - worldEntityProps.forEach(function(itemWE) { - if (itemWE.name.indexOf('-clone-' + cloneableProps.id) !== -1) { - count++; - } - }); - - var grabInfo = getGrabbableData(cloneableProps); - var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; - if (count >= limit && limit !== 0) { - return null; - } - - cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id; - var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; - var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; - var triggerable = grabInfo.triggerable ? grabInfo.triggerable : false; - var avatarEntity = grabInfo.cloneAvatarEntity ? grabInfo.cloneAvatarEntity : false; - var cUserData = Object.assign({}, JSON.parse(cloneableProps.userData)); - var cProperties = Object.assign({}, cloneableProps); - - - delete cUserData.grabbableKey.cloneLifetime; - delete cUserData.grabbableKey.cloneable; - delete cUserData.grabbableKey.cloneDynamic; - delete cUserData.grabbableKey.cloneLimit; - delete cUserData.grabbableKey.cloneAvatarEntity; - delete cProperties.id; - - - cProperties.dynamic = dynamic; - cProperties.locked = false; - cUserData.grabbableKey.triggerable = triggerable; - cUserData.grabbableKey.grabbable = true; - cProperties.lifetime = lifetime; - cProperties.userData = JSON.stringify(cUserData); - - var cloneID = Entities.addEntity(cProperties, avatarEntity); - return cloneID; + var entityToClone = props.id; + var certificateID = Entities.getEntityProperties(entityToClone, ['certificateID']).certificateID; + // ensure entity is cloneable and does not have a certificate ID, whereas cloneable limits + // will now be handled by the server where the entity add will fail if limit reached + if (entityIsCloneable(props) && (certificateID === undefined || certificateID.length === 0)) { + var cloneID = Entities.cloneEntity(entityToClone); + return cloneID; + } + return null; }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 71dc5e4273..f3f0e8dd01 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -125,7 +125,8 @@ DISPATCHER_PROPERTIES = [ "dimensions", "userData", "type", - "href" + "href", + "cloneable" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step From dea588ae4ac1968b0e3f5c070f9c99096fbc2b96 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 9 May 2018 18:12:04 -0700 Subject: [PATCH 02/29] remove prints --- libraries/entities/src/EntityScriptingInterface.cpp | 1 - libraries/entities/src/EntityTree.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 132fec2c51..7b7f35aa09 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -333,7 +333,6 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); if (addLocalEntityCopy(properties, newEntityID)) { - qCDebug(entities) << "DBACK POOPY cloneEntity addLocalEntityCopy" << newEntityID; getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); return newEntityID; } else { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1e8055ff46..20b3f81f42 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1659,7 +1659,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c if (isClone) { entityToClone->addCloneID(newEntity->getEntityItemID()); newEntity->setCloneParent(entityIDToClone); - qCDebug(entities) << "DBACK POOPY addedEntity clone " << newEntity->getEntityItemID(); } startLogging = usecTimestampNow(); From 1aa653c5db6cf05fbb94517c857e4b3958e8563a Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 9 May 2018 18:20:07 -0700 Subject: [PATCH 03/29] tabs --- scripts/system/html/js/entityProperties.js | 12 ++++++------ scripts/system/libraries/cloneEntityUtils.js | 16 ++++++++-------- .../libraries/controllerDispatcherUtils.js | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f0bcdad708..c81e51d674 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1045,7 +1045,7 @@ function loaded() { elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; elCloneableLimit.value = properties.cloneableLimit; elCloneableLifetime.value = properties.cloneableLifetime; - + var grabbablesSet = false; var parsedUserData = {}; try { @@ -1439,11 +1439,11 @@ function loaded() { } userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, true); }); - - elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); - elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneableDynamic')); - elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLifetime')); - elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLimit')); + + elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); + elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneableDynamic')); + elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLifetime')); + elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLimit')); elWantsTrigger.addEventListener('change', function() { userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false); diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index bd33d32342..358bc4a75f 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -47,13 +47,13 @@ propsAreCloneDynamic = function(props) { }; cloneEntity = function(props, worldEntityProps) { - var entityToClone = props.id; + var entityToClone = props.id; var certificateID = Entities.getEntityProperties(entityToClone, ['certificateID']).certificateID; - // ensure entity is cloneable and does not have a certificate ID, whereas cloneable limits - // will now be handled by the server where the entity add will fail if limit reached - if (entityIsCloneable(props) && (certificateID === undefined || certificateID.length === 0)) { - var cloneID = Entities.cloneEntity(entityToClone); - return cloneID; - } - return null; + // ensure entity is cloneable and does not have a certificate ID, whereas cloneable limits + // will now be handled by the server where the entity add will fail if limit reached + if (entityIsCloneable(props) && (certificateID === undefined || certificateID.length === 0)) { + var cloneID = Entities.cloneEntity(entityToClone); + return cloneID; + } + return null; }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index f3f0e8dd01..43945c6145 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -126,7 +126,7 @@ DISPATCHER_PROPERTIES = [ "userData", "type", "href", - "cloneable" + "cloneable" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step From 5aab88be06edf0b56fe4b9c6dd690e73ded6b529 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 9 May 2018 19:10:56 -0700 Subject: [PATCH 04/29] assert --- libraries/gpu/src/gpu/Buffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Buffer.cpp b/libraries/gpu/src/gpu/Buffer.cpp index fe3570e7f7..ebb768e597 100644 --- a/libraries/gpu/src/gpu/Buffer.cpp +++ b/libraries/gpu/src/gpu/Buffer.cpp @@ -99,7 +99,7 @@ Buffer::Update::Update(const Buffer& parent) : buffer(parent) { void Buffer::Update::apply() const { // Make sure we're loaded in order buffer._applyUpdateCount++; - //assert(buffer._applyUpdateCount == updateNumber); + assert(buffer._applyUpdateCount == updateNumber); const auto pageSize = buffer._pages._pageSize; buffer._renderSysmem.resize(size); From a57c280813afc5478148db440e3ab1812a5efac0 Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 11 May 2018 17:54:08 -0700 Subject: [PATCH 05/29] convertToCloneProperties --- libraries/entities/src/EntityEditPacketSender.cpp | 4 ---- libraries/entities/src/EntityItemProperties.cpp | 11 +++++++++++ libraries/entities/src/EntityItemProperties.h | 2 ++ libraries/entities/src/EntityScriptingInterface.cpp | 1 + libraries/entities/src/EntityTree.cpp | 9 +-------- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index a33f9763e7..c2323b0a20 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -156,10 +156,6 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI } void EntityEditPacketSender::queueCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID) { - if (!_shouldSend) { - return; // bail early - } - QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityClone), 0); if (EntityItemProperties::encodeCloneEntityMessage(entityIDToClone, newEntityID, bufferOut)) { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 93c2eb885e..7c799a64e1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3633,3 +3633,14 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8())); } + +void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityIDToClone) { + setName(getName() + "-clone-" + entityIDToClone.toString()); + setLocked(false); + setLifetime(getCloneableLifetime()); + setDynamic(getCloneableDynamic()); + setCloneable(ENTITY_ITEM_CLONEABLE); + setCloneableLifetime(ENTITY_ITEM_CLONEABLE_LIFETIME); + setCloneableLimit(ENTITY_ITEM_CLONEABLE_LIMIT); + setCloneableDynamic(ENTITY_ITEM_CLONEABLE_DYNAMIC); +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 3ae2186cab..12d4bdf919 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -376,6 +376,8 @@ public: bool verifyStaticCertificateProperties(); static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature); + void convertToCloneProperties(const EntityItemID& entityIDToClone); + protected: QString getCollisionMaskAsString() const; void setCollisionMaskFromString(const QString& maskString); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 7b7f35aa09..71e7d1c7c0 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -332,6 +332,7 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); + properties.convertToCloneProperties(entityIDToClone); if (addLocalEntityCopy(properties, newEntityID)) { getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); return newEntityID; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 20b3f81f42..bcea89232e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1623,14 +1623,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c qCDebug(entities) << "User attempted to clone entity ID:" << entityIDToClone << " which reached it's cloneable limit."; } else { if (isClone) { - properties.setName(properties.getName() + "-clone-" + entityIDToClone.toString()); - properties.setLocked(false); - properties.setLifetime(properties.getCloneableLifetime()); - properties.setDynamic(properties.getCloneableDynamic()); - properties.setCloneable(ENTITY_ITEM_CLONEABLE); - properties.setCloneableLifetime(ENTITY_ITEM_CLONEABLE_LIFETIME); - properties.setCloneableLimit(ENTITY_ITEM_CLONEABLE_LIMIT); - properties.setCloneableDynamic(ENTITY_ITEM_CLONEABLE_DYNAMIC); + properties.convertToCloneProperties(entityIDToClone); } // this is a new entity... assign a new entityID From f74e21902995af02fc39885eb2386868d38136ef Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 15 May 2018 17:24:14 -0700 Subject: [PATCH 06/29] rename properties, add clone avatar entity, remove worldEntityProps arg, fix clone ID cleanup on delete --- libraries/entities/src/EntityItem.cpp | 94 +++++++++++++------ libraries/entities/src/EntityItem.h | 28 +++--- .../entities/src/EntityItemProperties.cpp | 90 +++++++++++------- libraries/entities/src/EntityItemProperties.h | 8 +- .../src/EntityItemPropertiesDefaults.h | 8 +- libraries/entities/src/EntityPropertyFlags.h | 8 +- .../entities/src/EntityScriptingInterface.cpp | 3 +- libraries/entities/src/EntityTree.cpp | 38 +++++--- libraries/entities/src/EntityTree.h | 2 +- .../controllerModules/equipEntity.js | 3 +- .../controllerModules/nearActionGrabEntity.js | 3 +- .../controllerModules/nearParentGrabEntity.js | 3 +- scripts/system/html/js/entityProperties.js | 14 +-- scripts/system/libraries/cloneEntityUtils.js | 4 +- 14 files changed, 192 insertions(+), 114 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9d7e536f25..1e95e999b1 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -125,9 +125,11 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_LAST_EDITED_BY; requestedProperties += PROP_CLONEABLE; - requestedProperties += PROP_CLONEABLE_LIFETIME; - requestedProperties += PROP_CLONEABLE_LIMIT; - requestedProperties += PROP_CLONEABLE_DYNAMIC; + requestedProperties += PROP_CLONE_LIFETIME; + requestedProperties += PROP_CLONE_LIMIT; + requestedProperties += PROP_CLONE_DYNAMIC; + requestedProperties += PROP_CLONE_AVATAR_ENTITY; + requestedProperties += PROP_CLONE_ORIGIN_ID; return requestedProperties; } @@ -294,9 +296,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy()); APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, getCloneable()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, getCloneableLifetime()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, getCloneableLimit()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, getCloneableDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, getCloneLifetime()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -859,9 +863,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy); READ_ENTITY_PROPERTY(PROP_CLONEABLE, bool, setCloneable); - READ_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, float, setCloneableLifetime); - READ_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, float, setCloneableLimit); - READ_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, bool, setCloneableDynamic); + READ_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, float, setCloneLifetime); + READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit); + READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); + READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); + READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); @@ -1291,9 +1297,11 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy); COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneable, getCloneable); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableLifetime, getCloneableLifetime); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableLimit, getCloneableLimit); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableDynamic, getCloneableDynamic); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLifetime, getCloneLifetime); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLimit, getCloneLimit); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneDynamic, getCloneDynamic); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneAvatarEntity, getCloneAvatarEntity); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneOriginID, getCloneOriginID); properties._defaultSettings = false; @@ -1403,9 +1411,11 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneable, setCloneable); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableLifetime, setCloneableLifetime); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableLimit, setCloneableLimit); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableDynamic, setCloneableDynamic); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLifetime, setCloneLifetime); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLimit, setCloneLimit); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneDynamic, setCloneDynamic); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneAvatarEntity, setCloneAvatarEntity); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneOriginID, setCloneOriginID); if (updateQueryAACube()) { somethingChanged = true; @@ -3015,45 +3025,73 @@ void EntityItem::setCloneable(bool value) { }); } -float EntityItem::getCloneableLifetime() const { +float EntityItem::getCloneLifetime() const { float result; withReadLock([&] { - result = _cloneableLifetime; + result = _cloneLifetime; }); return result; } -void EntityItem::setCloneableLifetime(float value) { +void EntityItem::setCloneLifetime(float value) { withWriteLock([&] { - _cloneableLifetime = value; + _cloneLifetime = value; }); } -float EntityItem::getCloneableLimit() const { +float EntityItem::getCloneLimit() const { float result; withReadLock([&] { - result = _cloneableLimit; + result = _cloneLimit; }); return result; } -void EntityItem::setCloneableLimit(float value) { +void EntityItem::setCloneLimit(float value) { withWriteLock([&] { - _cloneableLimit = value; + _cloneLimit = value; }); } -bool EntityItem::getCloneableDynamic() const { +bool EntityItem::getCloneDynamic() const { bool result; withReadLock([&] { - result = _cloneableDynamic; + result = _cloneDynamic; }); return result; } -void EntityItem::setCloneableDynamic(const bool value) { +void EntityItem::setCloneDynamic(const bool value) { withWriteLock([&] { - _cloneableDynamic = value; + _cloneDynamic = value; + }); +} + +bool EntityItem::getCloneAvatarEntity() const { + bool result; + withReadLock([&] { + result = _cloneAvatarEntity; + }); + return result; +} + +void EntityItem::setCloneAvatarEntity(const bool value) { + withWriteLock([&] { + _cloneAvatarEntity = value; + }); +} + +const QUuid EntityItem::getCloneOriginID() const { + QUuid result; + withReadLock([&] { + result = _cloneOriginID; + }); + return result; +} + +void EntityItem::setCloneOriginID(const QUuid& value) { + withWriteLock([&] { + _cloneOriginID = value; }); } @@ -3072,4 +3110,4 @@ bool EntityItem::removeCloneID(const QUuid& cloneID) { return true; } return false; -} \ No newline at end of file +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 95cc5f96e1..f314cd1418 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -343,12 +343,16 @@ public: bool getCloneable() const; void setCloneable(bool value); - float getCloneableLifetime() const; - void setCloneableLifetime(float value); - float getCloneableLimit() const; - void setCloneableLimit(float value); - bool getCloneableDynamic() const; - void setCloneableDynamic(const bool value); + float getCloneLifetime() const; + void setCloneLifetime(float value); + float getCloneLimit() const; + void setCloneLimit(float value); + bool getCloneDynamic() const; + void setCloneDynamic(const bool value); + bool getCloneAvatarEntity() const; + void setCloneAvatarEntity(const bool value); + const QUuid getCloneOriginID() const; + void setCloneOriginID(const QUuid& value); // TODO: get rid of users of getRadius()... float getRadius() const; @@ -506,8 +510,6 @@ public: bool addCloneID(const QUuid& cloneID); bool removeCloneID(const QUuid& cloneID); const QList& getCloneIDs() const { return _cloneIDs; } - void setCloneParent(const QUuid& cloneParentID) { _cloneParentID = cloneParentID; } - const QUuid& getCloneParent() const { return _cloneParentID; } signals: void requestRenderUpdate(); @@ -664,12 +666,12 @@ protected: bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera bool _cloneable; - float _cloneableLifetime; - float _cloneableLimit; - bool _cloneableDynamic; - + float _cloneLifetime; + float _cloneLimit; + bool _cloneDynamic; + bool _cloneAvatarEntity; + QUuid _cloneOriginID; QList _cloneIDs; - QUuid _cloneParentID; private: std::unordered_map _materials; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7c799a64e1..11ef5a4a84 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -437,9 +437,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); CHECK_PROPERTY_CHANGE(PROP_CLONEABLE, cloneable); - CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIFETIME, cloneableLifetime); - CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIMIT, cloneableLimit); - CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic); + CHECK_PROPERTY_CHANGE(PROP_CLONE_LIFETIME, cloneLifetime); + CHECK_PROPERTY_CHANGE(PROP_CLONE_LIMIT, cloneLimit); + CHECK_PROPERTY_CHANGE(PROP_CLONE_DYNAMIC, cloneDynamic); + CHECK_PROPERTY_CHANGE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); + CHECK_PROPERTY_CHANGE(PROP_CLONE_ORIGIN_ID, cloneOriginID); changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); @@ -1436,9 +1438,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE, cloneable); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIFETIME, cloneableLifetime); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIMIT, cloneableLimit); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIFETIME, cloneLifetime); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIMIT, cloneLimit); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_DYNAMIC, cloneDynamic); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_ORIGIN_ID, cloneOriginID); // Rendering info if (!skipDefaults && !strictSemantics) { @@ -1653,9 +1657,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneable, bool, setCloneable); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLifetime, float, setCloneableLifetime); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLimit, float, setCloneableLimit); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableDynamic, bool, setCloneableDynamic); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLifetime, float, setCloneLifetime); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLimit, float, setCloneLimit); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneDynamic, bool, setCloneDynamic); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneAvatarEntity, bool, setCloneAvatarEntity); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneOriginID, QUuid, setCloneOriginID); _lastEdited = usecTimestampNow(); } @@ -2033,9 +2039,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t); ADD_PROPERTY_TO_MAP(PROP_CLONEABLE, Cloneable, cloneable, bool); - ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float); - ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float); - ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool); + ADD_PROPERTY_TO_MAP(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float); + ADD_PROPERTY_TO_MAP(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float); + ADD_PROPERTY_TO_MAP(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool); + ADD_PROPERTY_TO_MAP(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool); + ADD_PROPERTY_TO_MAP(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid); // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -2353,9 +2361,11 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion()); APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, properties.getCloneable()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, properties.getCloneableLifetime()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, properties.getCloneableLimit()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, properties.getCloneableDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, properties.getCloneLifetime()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, properties.getCloneOriginID()); } if (propertyCount > 0) { @@ -2727,9 +2737,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE, bool, setCloneable); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIFETIME, float, setCloneableLifetime); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIMIT, float, setCloneableLimit); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_DYNAMIC, bool, setCloneableDynamic); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIFETIME, float, setCloneLifetime); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIMIT, float, setCloneLimit); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); return valid; } @@ -2815,7 +2827,7 @@ bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityID char* copyAt = buffer.data(); int outputLength = 0; - if (buffer.size() < (int)(sizeof(NUM_BYTES_RFC4122_UUID) * 2)) { + if (buffer.size() < (int)(NUM_BYTES_RFC4122_UUID * 2)) { qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!"; return false; } @@ -3021,9 +3033,11 @@ void EntityItemProperties::markAllChanged() { _relayParentJointsChanged = true; _cloneableChanged = true; - _cloneableLifetimeChanged = true; - _cloneableLimitChanged = true; - _cloneableDynamicChanged = true; + _cloneLifetimeChanged = true; + _cloneLimitChanged = true; + _cloneDynamicChanged = true; + _cloneAvatarEntityChanged = true; + _cloneOriginIDChanged = true; } // The minimum bounding box for the entity. @@ -3459,14 +3473,20 @@ QList EntityItemProperties::listChangedProperties() { if (cloneableChanged()) { out += "cloneable"; } - if (cloneableLifetimeChanged()) { - out += "cloneableLifetime"; + if (cloneLifetimeChanged()) { + out += "cloneLifetime"; } - if (cloneableLimitChanged()) { - out += "cloneableLimit"; + if (cloneLimitChanged()) { + out += "cloneLimit"; } - if (cloneableDynamicChanged()) { - out += "cloneableDynamic"; + if (cloneDynamicChanged()) { + out += "cloneDynamic"; + } + if (cloneAvatarEntityChanged()) { + out += "cloneAvatarEntity"; + } + if (cloneOriginIDChanged()) { + out += "cloneOriginID"; } @@ -3637,10 +3657,12 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityIDToClone) { setName(getName() + "-clone-" + entityIDToClone.toString()); setLocked(false); - setLifetime(getCloneableLifetime()); - setDynamic(getCloneableDynamic()); + setLifetime(getCloneLifetime()); + setDynamic(getCloneDynamic()); + setClientOnly(getCloneAvatarEntity()); setCloneable(ENTITY_ITEM_CLONEABLE); - setCloneableLifetime(ENTITY_ITEM_CLONEABLE_LIFETIME); - setCloneableLimit(ENTITY_ITEM_CLONEABLE_LIMIT); - setCloneableDynamic(ENTITY_ITEM_CLONEABLE_DYNAMIC); -} \ No newline at end of file + setCloneLifetime(ENTITY_ITEM_CLONE_LIFETIME); + setCloneLimit(ENTITY_ITEM_CLONE_LIMIT); + setCloneDynamic(ENTITY_ITEM_CLONE_DYNAMIC); + setCloneAvatarEntity(ENTITY_ITEM_CLONE_AVATAR_ENTITY); +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 12d4bdf919..a34058c010 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -273,9 +273,11 @@ public: DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_CLONEABLE); - DEFINE_PROPERTY(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float, ENTITY_ITEM_CLONEABLE_LIFETIME); - DEFINE_PROPERTY(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float, ENTITY_ITEM_CLONEABLE_LIMIT); - DEFINE_PROPERTY(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool, ENTITY_ITEM_CLONEABLE_DYNAMIC); + DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_CLONE_LIFETIME); + DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_CLONE_LIMIT); + DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_CLONE_DYNAMIC); + DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_CLONE_AVATAR_ENTITY); + DEFINE_PROPERTY(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_CLONE_ORIGIN_ID); static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 6e46453bda..1a31bddebe 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -98,8 +98,10 @@ const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid(); const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false; const bool ENTITY_ITEM_CLONEABLE = false; -const float ENTITY_ITEM_CLONEABLE_LIFETIME = 300.0f; -const int ENTITY_ITEM_CLONEABLE_LIMIT = 0; -const bool ENTITY_ITEM_CLONEABLE_DYNAMIC = false; +const float ENTITY_ITEM_CLONE_LIFETIME = 300.0f; +const int ENTITY_ITEM_CLONE_LIMIT = 0; +const bool ENTITY_ITEM_CLONE_DYNAMIC = false; +const bool ENTITY_ITEM_CLONE_AVATAR_ENTITY = false; +const QUuid ENTITY_ITEM_CLONE_ORIGIN_ID = QUuid(); #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index f698739e01..85ab28afdc 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -243,9 +243,11 @@ enum EntityPropertyList { PROP_MATERIAL_DATA, PROP_CLONEABLE, - PROP_CLONEABLE_LIFETIME, - PROP_CLONEABLE_LIMIT, - PROP_CLONEABLE_DYNAMIC, + PROP_CLONE_LIFETIME, + PROP_CLONE_LIMIT, + PROP_CLONE_DYNAMIC, + PROP_CLONE_AVATAR_ENTITY, + PROP_CLONE_ORIGIN_ID, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 71e7d1c7c0..f47a353ae8 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -333,7 +333,8 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); properties.convertToCloneProperties(entityIDToClone); - if (addLocalEntityCopy(properties, newEntityID)) { + bool success = addLocalEntityCopy(properties, newEntityID); + if (success) { getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); return newEntityID; } else { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bcea89232e..647edaccc0 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -593,7 +593,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign return; } - removeCloneIDFromCloneParent(entityID); + cleanupCloneIDs(entityID); unhookChildAvatar(entityID); emit deletingEntity(entityID); emit deletingEntityPointer(existingEntity.get()); @@ -627,14 +627,23 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) { }); } -void EntityTree::removeCloneIDFromCloneParent(const EntityItemID& entityID) { +void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { EntityItemPointer entity = findEntityByEntityItemID(entityID); if (entity) { - const QUuid& cloneParentID = entity->getCloneParent(); - if (!cloneParentID.isNull()) { - EntityItemPointer cloneParent = findEntityByID(cloneParentID); - if (cloneParent) { - cloneParent->removeCloneID(entityID); + // remove clone ID from it's clone origin's clone ID list if clone origin exists + const QUuid cloneOriginID = entity->getCloneOriginID(); + if (!cloneOriginID.isNull()) { + EntityItemPointer cloneOrigin = findEntityByID(cloneOriginID); + if (cloneOrigin) { + cloneOrigin->removeCloneID(entityID); + } + } + // clear the clone origin ID on any clones that this entity had + const QList& cloneIDs = entity->getCloneIDs(); + foreach(const QUuid& cloneChildID, cloneIDs) { + EntityItemPointer cloneChild = findEntityByEntityItemID(cloneChildID); + if (cloneChild) { + cloneChild->setCloneOriginID(QUuid()); } } } @@ -668,7 +677,7 @@ void EntityTree::deleteEntities(QSet entityIDs, bool force, bool i } // tell our delete operator about this entityID - removeCloneIDFromCloneParent(entityID); + cleanupCloneIDs(entityID); unhookChildAvatar(entityID); theOperator.addEntityIDToDeleteList(entityID); emit deletingEntity(entityID); @@ -1601,7 +1610,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c bool failedAdd = !allowed; bool isCertified = !properties.getCertificateID().isEmpty(); bool isCloneable = properties.getCloneable(); - int cloneLimit = properties.getCloneableLimit(); + int cloneLimit = properties.getCloneLimit(); if (!allowed) { qCDebug(entities) << "Filtered entity add. ID:" << entityItemID; } else if (!isClone && !isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) { @@ -1646,14 +1655,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } + if (newEntity && isClone) { + entityToClone->addCloneID(newEntity->getEntityItemID()); + newEntity->setCloneOriginID(entityIDToClone); + } + if (newEntity) { newEntity->markAsChangedOnServer(); notifyNewlyCreatedEntity(*newEntity, senderNode); - if (isClone) { - entityToClone->addCloneID(newEntity->getEntityItemID()); - newEntity->setCloneParent(entityIDToClone); - } - + startLogging = usecTimestampNow(); if (wantEditLogging()) { qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:" diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 0d7cc54df7..aa6e382112 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -117,7 +117,7 @@ public: // check if the avatar is a child of this entity, If so set the avatar parentID to null void unhookChildAvatar(const EntityItemID entityID); - void removeCloneIDFromCloneParent(const EntityItemID& entityID); + void cleanupCloneIDs(const EntityItemID& entityID); void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true); void deleteEntities(QSet entityIDs, bool force = false, bool ignoreWarnings = true); diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 1fce772ec8..b6cad6841d 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -413,8 +413,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.cloneHotspot = function(props, controllerData) { if (entityIsCloneable(props)) { - var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; - var cloneID = cloneEntity(props, worldEntityProps); + var cloneID = cloneEntity(props); return cloneID; } diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index a4e439fe2f..5e06ed762d 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -235,8 +235,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); // switch to grabbing var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { - var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; - var cloneID = cloneEntity(targetProps, worldEntityProps); + var cloneID = cloneEntity(targetProps); var cloneProps = Entities.getEntityProperties(cloneID); this.targetEntityID = cloneID; this.startNearGrabAction(controllerData, cloneProps); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index d454d20a02..cda0a683c7 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -342,8 +342,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { - var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; - var cloneID = cloneEntity(targetProps, worldEntityProps); + var cloneID = cloneEntity(targetProps); var cloneProps = Entities.getEntityProperties(cloneID); this.grabbing = true; this.targetEntityID = cloneID; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index c81e51d674..10e7b4b60e 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1041,10 +1041,11 @@ function loaded() { elIgnoreIK.checked = true; elCloneable.checked = properties.cloneable; - elCloneableDynamic.checked = properties.cloneableDynamic; + elCloneableDynamic.checked = properties.cloneDynamic; + elCloneableAvatarEntity.checked = properties.cloneAvatarEntity; elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; - elCloneableLimit.value = properties.cloneableLimit; - elCloneableLifetime.value = properties.cloneableLifetime; + elCloneableLimit.value = properties.cloneLimit; + elCloneableLifetime.value = properties.cloneLifetime; var grabbablesSet = false; var parsedUserData = {}; @@ -1441,9 +1442,10 @@ function loaded() { }); elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); - elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneableDynamic')); - elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLifetime')); - elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLimit')); + elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneDynamic')); + elCloneableAvatarEntity.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneAvatarEntity')); + elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLifetime')); + elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLimit')); elWantsTrigger.addEventListener('change', function() { userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false); diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index 358bc4a75f..5381d39116 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -41,12 +41,12 @@ entityIsCloneable = function(props) { propsAreCloneDynamic = function(props) { var cloneable = entityIsCloneable(props); if (cloneable) { - return props.cloneableDynamic; + return props.cloneDynamic; } return false; }; -cloneEntity = function(props, worldEntityProps) { +cloneEntity = function(props) { var entityToClone = props.id; var certificateID = Entities.getEntityProperties(entityToClone, ['certificateID']).certificateID; // ensure entity is cloneable and does not have a certificate ID, whereas cloneable limits From 1e2d2819d512056db75701321e0674ec2c031a41 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 15 May 2018 17:36:39 -0700 Subject: [PATCH 07/29] tweaks --- libraries/entities/src/EntityItemProperties.cpp | 3 --- libraries/entities/src/EntityTree.cpp | 2 +- scripts/system/html/js/entityProperties.js | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 11ef5a4a84..5a2f3a8fc5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2823,7 +2823,6 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt } bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityIDToClone, const EntityItemID& newEntityID, QByteArray& buffer) { - char* copyAt = buffer.data(); int outputLength = 0; @@ -2846,7 +2845,6 @@ bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityID } bool EntityItemProperties::decodeCloneEntityMessage(const QByteArray& buffer, int& processedBytes, EntityItemID& entityIDToClone, EntityItemID& newEntityID) { - const unsigned char* packetData = (const unsigned char*)buffer.constData(); const unsigned char* dataAt = packetData; size_t packetLength = buffer.size(); @@ -3489,7 +3487,6 @@ QList EntityItemProperties::listChangedProperties() { out += "cloneOriginID"; } - getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); getAmbientLight().listChangedProperties(out); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 647edaccc0..b2daed4e72 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1429,7 +1429,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c case PacketType::EntityClone: isClone = true; // fall through to next case case PacketType::EntityAdd: - isAdd = true; // fall through to next case + isAdd = true; // fall through to next case // FALLTHRU case PacketType::EntityPhysics: case PacketType::EntityEdit: { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index e016a21731..4271aa9b09 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1042,7 +1042,7 @@ function loaded() { elCloneable.checked = properties.cloneable; elCloneableDynamic.checked = properties.cloneDynamic; - elCloneableAvatarEntity.checked = properties.cloneAvatarEntity; + elCloneableAvatarEntity.checked = properties.cloneAvatarEntity; elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; elCloneableLimit.value = properties.cloneLimit; elCloneableLifetime.value = properties.cloneLifetime; @@ -1443,7 +1443,7 @@ function loaded() { elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneDynamic')); - elCloneableAvatarEntity.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneAvatarEntity')); + elCloneableAvatarEntity.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneAvatarEntity')); elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLifetime')); elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLimit')); From 6c946d7ac5e7f9da7640ef21258f7d60e944609c Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 16 May 2018 11:36:57 -0700 Subject: [PATCH 08/29] prevent continuous clone calls --- .../controllerModules/nearParentGrabEntity.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 0ebd4efb2f..5f310e471f 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -35,6 +35,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.lastUnexpectedChildrenCheckTime = 0; this.robbed = false; this.highlightedEntity = null; + this.cloneAllowed = true; this.parameters = makeDispatcherModuleParameters( 500, @@ -272,6 +273,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { this.checkForUnexpectedChildren(controllerData); this.robbed = false; + this.cloneAllowed = true; return makeRunningValues(false, [], []); } @@ -335,12 +337,14 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { - var cloneID = cloneEntity(targetProps); - var cloneProps = Entities.getEntityProperties(cloneID); - this.grabbing = true; - this.targetEntityID = cloneID; - this.startNearParentingGrabEntity(controllerData, cloneProps); - + if (this.cloneAllowed) { + var cloneID = cloneEntity(targetProps); + var cloneProps = Entities.getEntityProperties(cloneID); + this.grabbing = true; + this.targetEntityID = cloneID; + this.startNearParentingGrabEntity(controllerData, cloneProps); + this.cloneAllowed = false; + } } else if (targetProps) { this.grabbing = true; this.startNearParentingGrabEntity(controllerData, targetProps); From 06fa4efe055240e900c109bd1b5a9ba705b327b7 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 16 May 2018 11:39:53 -0700 Subject: [PATCH 09/29] add comment --- .../controllers/controllerModules/nearParentGrabEntity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 5f310e471f..4ab45b33f7 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -343,7 +343,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.grabbing = true; this.targetEntityID = cloneID; this.startNearParentingGrabEntity(controllerData, cloneProps); - this.cloneAllowed = false; + this.cloneAllowed = false; // prevent another clone call until inputs released } } else if (targetProps) { this.grabbing = true; From 3730cbe36c829fa46a2bd83d340e5e2f21bf5d4c Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 17 May 2018 10:28:48 -0700 Subject: [PATCH 10/29] reconstruct cloneIDs lists on start, read/write locks on clone ID functions, fix removing last cloneable --- libraries/entities/src/EntityItem.cpp | 36 ++++++++++++++++++++------- libraries/entities/src/EntityItem.h | 3 ++- libraries/entities/src/EntityTree.cpp | 18 +++++++++++++- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1e95e999b1..e6aa572330 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3096,18 +3096,36 @@ void EntityItem::setCloneOriginID(const QUuid& value) { } bool EntityItem::addCloneID(const QUuid& cloneID) { - if (!_cloneIDs.contains(cloneID)) { - _cloneIDs.append(cloneID); - return true; - } + withWriteLock([&] { + if (!_cloneIDs.contains(cloneID)) { + _cloneIDs.append(cloneID); + return true; + } + }); return false; } bool EntityItem::removeCloneID(const QUuid& cloneID) { - int index = _cloneIDs.indexOf(cloneID); - if (index > 0) { - _cloneIDs.removeAt(index); - return true; - } + withWriteLock([&] { + int index = _cloneIDs.indexOf(cloneID); + if (index >= 0) { + _cloneIDs.removeAt(index); + return true; + } + }); return false; } + +const QList EntityItem::getCloneIDs() const { + QList result; + withReadLock([&] { + result = _cloneIDs; + }); + return result; +} + +void EntityItem::setCloneIDs(const QList& cloneIDs) { + withWriteLock([&] { + _cloneIDs = cloneIDs; + }); +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index f314cd1418..cc8cefb17b 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -509,7 +509,8 @@ public: bool addCloneID(const QUuid& cloneID); bool removeCloneID(const QUuid& cloneID); - const QList& getCloneIDs() const { return _cloneIDs; } + const QList getCloneIDs() const; + void setCloneIDs(const QList& cloneIDs); signals: void requestRenderUpdate(); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b2daed4e72..c87f6d771a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -631,7 +631,7 @@ void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { EntityItemPointer entity = findEntityByEntityItemID(entityID); if (entity) { // remove clone ID from it's clone origin's clone ID list if clone origin exists - const QUuid cloneOriginID = entity->getCloneOriginID(); + const QUuid& cloneOriginID = entity->getCloneOriginID(); if (!cloneOriginID.isNull()) { EntityItemPointer cloneOrigin = findEntityByID(cloneOriginID); if (cloneOrigin) { @@ -2385,6 +2385,8 @@ bool EntityTree::readFromMap(QVariantMap& map) { return false; } + QMap> cloneIDs; + bool success = true; foreach (QVariant entityVariant, entitiesQList) { // QVariantMap --> QScriptValue --> EntityItemProperties --> Entity @@ -2477,6 +2479,20 @@ bool EntityTree::readFromMap(QVariantMap& map) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); success = false; } + + const QUuid& cloneOriginID = entity->getCloneOriginID(); + if (!cloneOriginID.isNull()) { + cloneIDs[cloneOriginID].push_back(entity->getEntityItemID()); + } + } + + for (auto iter = cloneIDs.begin(); iter != cloneIDs.end(); ++iter) { + const QUuid& entityID = iter.key(); + const QList& cloneIDs = iter.value(); + EntityItemPointer entity = findEntityByID(entityID); + if (entity) { + entity->setCloneIDs(cloneIDs); + } } return success; From 42d84924274def80ade80cafeb5b7d84aaa3ec44 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 17 May 2018 11:17:04 -0700 Subject: [PATCH 11/29] no clock skew --- libraries/entities/src/EntityEditPacketSender.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index c2323b0a20..9ca102d016 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -34,7 +34,7 @@ void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer Date: Thu, 17 May 2018 11:25:20 -0700 Subject: [PATCH 12/29] Add JSDoc header to new clone function in Entity API --- libraries/entities/src/EntityScriptingInterface.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 883990418a..febc33ffde 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -224,6 +224,14 @@ public slots: Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic, bool collisionless, const glm::vec3& position, const glm::vec3& gravity); + /**jsdoc + * Request a clone of an entity. Only entities that have been marked as 'cloneable' will be able to be cloned using this method. + * A cloned entity has most of the properties of the orignal entity, and can be requested from clients that do not have rez permissions. + * The client requests a clone from the entity server, which returns back the entityID of a valid clone if the operation was allowed. + * @function Entities.cloneEntity + * @param {Uuid} entityIDToClone - the ID of the entity to clone + * @returns {Entities.EntityID} The ID of the newly created clone + */ Q_INVOKABLE QUuid cloneEntity(QUuid entityIDToClone); /**jsdoc From b8ce07744da6d0c2c8a963644172529038cd0024 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 17 May 2018 14:50:14 -0700 Subject: [PATCH 13/29] initialize prop defaults, no bool returns --- libraries/entities/src/EntityItem.cpp | 8 ++------ libraries/entities/src/EntityItem.h | 14 +++++++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index e6aa572330..667ca9296d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3095,25 +3095,21 @@ void EntityItem::setCloneOriginID(const QUuid& value) { }); } -bool EntityItem::addCloneID(const QUuid& cloneID) { +void EntityItem::addCloneID(const QUuid& cloneID) { withWriteLock([&] { if (!_cloneIDs.contains(cloneID)) { _cloneIDs.append(cloneID); - return true; } }); - return false; } -bool EntityItem::removeCloneID(const QUuid& cloneID) { +void EntityItem::removeCloneID(const QUuid& cloneID) { withWriteLock([&] { int index = _cloneIDs.indexOf(cloneID); if (index >= 0) { _cloneIDs.removeAt(index); - return true; } }); - return false; } const QList EntityItem::getCloneIDs() const { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index cc8cefb17b..9b1230c843 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -507,8 +507,8 @@ public: void setSimulationOwnershipExpiry(uint64_t expiry) { _simulationOwnershipExpiry = expiry; } uint64_t getSimulationOwnershipExpiry() const { return _simulationOwnershipExpiry; } - bool addCloneID(const QUuid& cloneID); - bool removeCloneID(const QUuid& cloneID); + void addCloneID(const QUuid& cloneID); + void removeCloneID(const QUuid& cloneID); const QList getCloneIDs() const; void setCloneIDs(const QList& cloneIDs); @@ -666,11 +666,11 @@ protected: bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera - bool _cloneable; - float _cloneLifetime; - float _cloneLimit; - bool _cloneDynamic; - bool _cloneAvatarEntity; + bool _cloneable { ENTITY_ITEM_CLONEABLE }; + float _cloneLifetime { ENTITY_ITEM_CLONE_LIFETIME }; + float _cloneLimit { ENTITY_ITEM_CLONE_LIMIT }; + bool _cloneDynamic { ENTITY_ITEM_CLONE_DYNAMIC }; + bool _cloneAvatarEntity { ENTITY_ITEM_CLONE_AVATAR_ENTITY }; QUuid _cloneOriginID; QList _cloneIDs; From 30855f94eb3baed58067ad7d1af8bba1086d9726 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 17 May 2018 16:02:58 -0700 Subject: [PATCH 14/29] set last edited to 0 to ensure server update --- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f47a353ae8..905474fd68 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -333,6 +333,7 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); properties.convertToCloneProperties(entityIDToClone); + properties.setLastEdited(0); // to ensure we take the properties from the server-created entity bool success = addLocalEntityCopy(properties, newEntityID); if (success) { getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); From 4e14eb37e304d1216545eb5ba44ccb7187b5bac3 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 17 May 2018 16:34:19 -0700 Subject: [PATCH 15/29] update comment --- libraries/entities/src/EntityScriptingInterface.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 905474fd68..ca64b3b09d 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -333,7 +333,9 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); properties.convertToCloneProperties(entityIDToClone); - properties.setLastEdited(0); // to ensure we take the properties from the server-created entity + // setLastEdited timestamp to 0 to ensure this entity gets updated with the properties + // from the server-created entity, don't change this unless you know what you are doing + properties.setLastEdited(0); bool success = addLocalEntityCopy(properties, newEntityID); if (success) { getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); From 29b11b910124640c39e59131de2e2443d904368b Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Fri, 18 May 2018 17:12:41 -0700 Subject: [PATCH 16/29] some fixes to server cloneables --- libraries/entities/src/EntityItem.cpp | 6 +++--- libraries/entities/src/EntityItem.h | 16 ++++++++-------- libraries/entities/src/EntityItemProperties.cpp | 10 +++++----- libraries/entities/src/EntityItemProperties.h | 12 ++++++------ .../entities/src/EntityItemPropertiesDefaults.h | 12 ++++++------ .../entities/src/EntityScriptingInterface.cpp | 17 ++++++++++++----- libraries/entities/src/EntityTree.cpp | 9 +++++---- 7 files changed, 45 insertions(+), 37 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 667ca9296d..9bad6f2519 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3112,15 +3112,15 @@ void EntityItem::removeCloneID(const QUuid& cloneID) { }); } -const QList EntityItem::getCloneIDs() const { - QList result; +const QVector EntityItem::getCloneIDs() const { + QVector result; withReadLock([&] { result = _cloneIDs; }); return result; } -void EntityItem::setCloneIDs(const QList& cloneIDs) { +void EntityItem::setCloneIDs(const QVector& cloneIDs) { withWriteLock([&] { _cloneIDs = cloneIDs; }); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 9b1230c843..42c4605e44 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -509,8 +509,8 @@ public: void addCloneID(const QUuid& cloneID); void removeCloneID(const QUuid& cloneID); - const QList getCloneIDs() const; - void setCloneIDs(const QList& cloneIDs); + const QVector getCloneIDs() const; + void setCloneIDs(const QVector& cloneIDs); signals: void requestRenderUpdate(); @@ -666,13 +666,13 @@ protected: bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera - bool _cloneable { ENTITY_ITEM_CLONEABLE }; - float _cloneLifetime { ENTITY_ITEM_CLONE_LIFETIME }; - float _cloneLimit { ENTITY_ITEM_CLONE_LIMIT }; - bool _cloneDynamic { ENTITY_ITEM_CLONE_DYNAMIC }; - bool _cloneAvatarEntity { ENTITY_ITEM_CLONE_AVATAR_ENTITY }; + bool _cloneable { ENTITY_ITEM_DEFAULT_CLONEABLE }; + float _cloneLifetime { ENTITY_ITEM_DEFAULT_CLONE_LIFETIME }; + float _cloneLimit { ENTITY_ITEM_DEFAULT_CLONE_LIMIT }; + bool _cloneDynamic { ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC }; + bool _cloneAvatarEntity { ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY }; QUuid _cloneOriginID; - QList _cloneIDs; + QVector _cloneIDs; private: std::unordered_map _materials; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5a2f3a8fc5..39449c7cd1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3657,9 +3657,9 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setLifetime(getCloneLifetime()); setDynamic(getCloneDynamic()); setClientOnly(getCloneAvatarEntity()); - setCloneable(ENTITY_ITEM_CLONEABLE); - setCloneLifetime(ENTITY_ITEM_CLONE_LIFETIME); - setCloneLimit(ENTITY_ITEM_CLONE_LIMIT); - setCloneDynamic(ENTITY_ITEM_CLONE_DYNAMIC); - setCloneAvatarEntity(ENTITY_ITEM_CLONE_AVATAR_ENTITY); + setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE); + setCloneLifetime(ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); + setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT); + setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); + setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a34058c010..d00a21435c 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -272,12 +272,12 @@ public: DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); - DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_CLONEABLE); - DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_CLONE_LIFETIME); - DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_CLONE_LIMIT); - DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_CLONE_DYNAMIC); - DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_CLONE_AVATAR_ENTITY); - DEFINE_PROPERTY(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_CLONE_ORIGIN_ID); + DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); + DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); + DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); + DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); + DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); + DEFINE_PROPERTY(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 1a31bddebe..0fd926e677 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -97,11 +97,11 @@ const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid(); const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false; -const bool ENTITY_ITEM_CLONEABLE = false; -const float ENTITY_ITEM_CLONE_LIFETIME = 300.0f; -const int ENTITY_ITEM_CLONE_LIMIT = 0; -const bool ENTITY_ITEM_CLONE_DYNAMIC = false; -const bool ENTITY_ITEM_CLONE_AVATAR_ENTITY = false; -const QUuid ENTITY_ITEM_CLONE_ORIGIN_ID = QUuid(); +const bool ENTITY_ITEM_DEFAULT_CLONEABLE = false; +const float ENTITY_ITEM_DEFAULT_CLONE_LIFETIME = 300.0f; +const int ENTITY_ITEM_DEFAULT_CLONE_LIMIT = 0; +const bool ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC = false; +const bool ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY = false; +const QUuid ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID = QUuid(); #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f47a353ae8..ace4670fef 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -332,13 +332,20 @@ QUuid EntityScriptingInterface::addModelEntity(const QString& name, const QStrin QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); + bool cloneAvatarEntity = properties.getCloneAvatarEntity(); properties.convertToCloneProperties(entityIDToClone); - bool success = addLocalEntityCopy(properties, newEntityID); - if (success) { - getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); - return newEntityID; + + if (cloneAvatarEntity) { + return addEntity(properties, true); } else { - return QUuid(); + bool success = addLocalEntityCopy(properties, newEntityID); + if (success) { + getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); + return newEntityID; + } + else { + return QUuid(); + } } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c87f6d771a..a36ee4be9d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -639,7 +639,7 @@ void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { } } // clear the clone origin ID on any clones that this entity had - const QList& cloneIDs = entity->getCloneIDs(); + const QVector& cloneIDs = entity->getCloneIDs(); foreach(const QUuid& cloneChildID, cloneIDs) { EntityItemPointer cloneChild = findEntityByEntityItemID(cloneChildID); if (cloneChild) { @@ -1627,7 +1627,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } else if (isClone && !isCloneable) { failedAdd = true; qCDebug(entities) << "User attempted to clone non-cloneable entity from entity ID:" << entityIDToClone; - } else if (isClone && entityToClone && entityToClone->getCloneIDs().size() >= cloneLimit) { + } else if (isClone && entityToClone && entityToClone->getCloneIDs().size() >= cloneLimit && cloneLimit != 0) { failedAdd = true; qCDebug(entities) << "User attempted to clone entity ID:" << entityIDToClone << " which reached it's cloneable limit."; } else { @@ -2039,6 +2039,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons if (shouldEraseEntity(entityID, sourceNode)) { entityItemIDsToDelete << entityItemID; + cleanupCloneIDs(entityItemID); } } @@ -2385,7 +2386,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { return false; } - QMap> cloneIDs; + QMap> cloneIDs; bool success = true; foreach (QVariant entityVariant, entitiesQList) { @@ -2488,7 +2489,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { for (auto iter = cloneIDs.begin(); iter != cloneIDs.end(); ++iter) { const QUuid& entityID = iter.key(); - const QList& cloneIDs = iter.value(); + const QVector& cloneIDs = iter.value(); EntityItemPointer entity = findEntityByID(entityID); if (entity) { entity->setCloneIDs(cloneIDs); From 6c8d68afc61ccc917f019664f7bed1b1f6a9d2ff Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Fri, 18 May 2018 18:18:42 -0700 Subject: [PATCH 17/29] add extra cleanup in delete messages and simulation, as a fall back --- libraries/entities/src/EntitySimulation.cpp | 1 + libraries/entities/src/EntityTree.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index d034ddedbe..ba088cb7fd 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -65,6 +65,7 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) { removeEntityInternal(entity); if (entity->getElement()) { _deadEntities.insert(entity); + _entityTree->cleanupCloneIDs(entity->getEntityItemID()); } } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a36ee4be9d..b698ac6eb8 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1990,6 +1990,7 @@ int EntityTree::processEraseMessage(ReceivedMessage& message, const SharedNodePo if (shouldEraseEntity(entityID, sourceNode)) { entityItemIDsToDelete << entityItemID; + cleanupCloneIDs(entityItemID); } } deleteEntities(entityItemIDsToDelete, true, true); From 9c0084209b51fd89a9e9b8728dae418190cbd85f Mon Sep 17 00:00:00 2001 From: Livi Erickson Date: Sun, 20 May 2018 16:41:14 -0700 Subject: [PATCH 18/29] fix style error, start lifetime properly for cloned entities --- libraries/entities/src/EntityItemProperties.cpp | 2 ++ libraries/entities/src/EntityTree.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 39449c7cd1..8dc9e94096 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3657,6 +3657,8 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setLifetime(getCloneLifetime()); setDynamic(getCloneDynamic()); setClientOnly(getCloneAvatarEntity()); + setCreated(usecTimestampNow()); + setLastEdited(usecTimestampNow()); setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE); setCloneLifetime(ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b698ac6eb8..52e8fe164b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1429,6 +1429,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c case PacketType::EntityClone: isClone = true; // fall through to next case case PacketType::EntityAdd: + // FALLTHRU isAdd = true; // fall through to next case // FALLTHRU case PacketType::EntityPhysics: From 9f7b38375771d803223bd9493893a2f36da30ac3 Mon Sep 17 00:00:00 2001 From: Livi Erickson Date: Sun, 20 May 2018 18:22:17 -0700 Subject: [PATCH 19/29] address CR feedback --- libraries/entities/src/EntityTree.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 52e8fe164b..8fa13c5618 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1457,12 +1457,13 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c if (isClone) { QByteArray buffer = QByteArray::fromRawData(reinterpret_cast(editData), maxLength); validEditPacket = EntityItemProperties::decodeCloneEntityMessage(buffer, processedBytes, entityIDToClone, entityItemID); - entityToClone = findEntityByEntityItemID(entityIDToClone); - if (entityToClone) { - properties = entityToClone->getProperties(); + if (validEditPacket) { + entityToClone = findEntityByEntityItemID(entityIDToClone); + if (entityToClone) { + properties = entityToClone->getProperties(); + } } - } - else { + } else { validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties); } From f4df75d016633eaa71a2286fda7bd16e1eaa50a7 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 21 May 2018 06:04:05 +0200 Subject: [PATCH 20/29] CR feedback --- libraries/entities/src/EntityScriptingInterface.cpp | 3 +-- libraries/entities/src/EntityTree.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index dfc1dd9585..bf606538fd 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -293,8 +293,7 @@ bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properti // since we're creating this object we will immediately volunteer to own its simulation entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY); properties.setLastEdited(entity->getLastEdited()); - } - else { + } else { qCDebug(entities) << "script failed to add new Entity to local Octree"; success = false; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8fa13c5618..ee3945e5cf 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1428,8 +1428,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c case PacketType::EntityClone: isClone = true; // fall through to next case - case PacketType::EntityAdd: // FALLTHRU + case PacketType::EntityAdd: isAdd = true; // fall through to next case // FALLTHRU case PacketType::EntityPhysics: @@ -2490,12 +2490,10 @@ bool EntityTree::readFromMap(QVariantMap& map) { } } - for (auto iter = cloneIDs.begin(); iter != cloneIDs.end(); ++iter) { - const QUuid& entityID = iter.key(); - const QVector& cloneIDs = iter.value(); - EntityItemPointer entity = findEntityByID(entityID); + for (const auto& entityID : cloneIDs.keys()) { + auto entity = findEntityByID(entityID); if (entity) { - entity->setCloneIDs(cloneIDs); + entity->setCloneIDs(cloneIDs.value(entityID)); } } From 7f4e9a0089370bcb4812f9985f9131ac7f3225b6 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 21 May 2018 06:17:08 +0200 Subject: [PATCH 21/29] style --- libraries/entities/src/EntityScriptingInterface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index bf606538fd..f1eb3862a1 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -344,8 +344,7 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { if (success) { getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); return newEntityID; - } - else { + } else { return QUuid(); } } From 604b3cac1c7a5f1060f1701fb8a31305ab3eb1c0 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 21 May 2018 16:19:43 -0700 Subject: [PATCH 22/29] check for null entities in grab, add conversion for old userdata cloneable --- libraries/entities/src/EntityTree.cpp | 20 +++++++++++++++++++ .../controllerModules/nearParentGrabEntity.js | 12 ++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ee3945e5cf..dff51a189b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2478,6 +2478,26 @@ bool EntityTree::readFromMap(QVariantMap& map) { } } + // Convert old cloneable entities so they use cloneableData instead of userData + if (contentVersion < (int)EntityVersion::CloneableData) { + QJsonObject userData = QJsonDocument::fromJson(properties.getUserData().toUtf8()).object(); + QJsonObject grabbableKey = userData["grabbableKey"].toObject(); + QJsonValue cloneable = grabbableKey["cloneable"]; + if (cloneable.isBool() && cloneable.toBool()) { + QJsonValue cloneLifetime = grabbableKey["cloneLifetime"]; + QJsonValue cloneLimit = grabbableKey["cloneLimit"]; + QJsonValue cloneDynamic = grabbableKey["cloneDynamic"]; + QJsonValue cloneAvatarEntity = grabbableKey["cloneAvatarEntity"]; + + // This is cloneable, we need to convert the properties + properties.setCloneable(true); + properties.setCloneLifetime(cloneLifetime.toInt()); + properties.setCloneLimit(cloneLimit.toInt()); + properties.setCloneDynamic(cloneDynamic.toBool()); + properties.setCloneAvatarEntity(cloneAvatarEntity.toBool()); + } + } + EntityItemPointer entity = addEntity(entityItemID, properties); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 4ab45b33f7..00d7ad0491 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -339,11 +339,13 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (targetCloneable) { if (this.cloneAllowed) { var cloneID = cloneEntity(targetProps); - var cloneProps = Entities.getEntityProperties(cloneID); - this.grabbing = true; - this.targetEntityID = cloneID; - this.startNearParentingGrabEntity(controllerData, cloneProps); - this.cloneAllowed = false; // prevent another clone call until inputs released + if (cloneID !== null) { + var cloneProps = Entities.getEntityProperties(cloneID); + this.grabbing = true; + this.targetEntityID = cloneID; + this.startNearParentingGrabEntity(controllerData, cloneProps); + this.cloneAllowed = false; // prevent another clone call until inputs released + } } } else if (targetProps) { this.grabbing = true; From e76829bb1de341fff96fc40a6bda20d124a70916 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 21 May 2018 16:51:23 -0700 Subject: [PATCH 23/29] change to DEFINE_PROPERTY_REF --- libraries/entities/src/EntityItemProperties.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index d00a21435c..9a0ec90450 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -272,12 +272,12 @@ public: DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); - DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); - DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); - DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); - DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); - DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); - DEFINE_PROPERTY(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); + DEFINE_PROPERTY_REF(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); + DEFINE_PROPERTY_REF(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); + DEFINE_PROPERTY_REF(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); + DEFINE_PROPERTY_REF(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); + DEFINE_PROPERTY_REF(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); + DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); From a1e7f648bc13c94ff522916fdfc0d0bf2461eb63 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 21 May 2018 17:14:36 -0700 Subject: [PATCH 24/29] add debug properties --- libraries/entities/src/EntityItemProperties.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 9a0ec90450..3286f78917 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -526,6 +526,13 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLimit, cloneLimit, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneDynamic, cloneDynamic, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneAvatarEntity, cloneAvatarEntity, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneOriginID, cloneOriginID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); From 27c2bb90467bec96e35bb24c9fc81a0ba209ddb1 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 21 May 2018 17:39:08 -0700 Subject: [PATCH 25/29] add properties group where missing --- libraries/entities/src/EntityItemProperties.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 8dc9e94096..e9179d8adc 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1814,6 +1814,13 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(dpi); + COPY_PROPERTY_IF_CHANGED(cloneable); + COPY_PROPERTY_IF_CHANGED(cloneLifetime); + COPY_PROPERTY_IF_CHANGED(cloneLimit); + COPY_PROPERTY_IF_CHANGED(cloneDynamic); + COPY_PROPERTY_IF_CHANGED(cloneAvatarEntity); + COPY_PROPERTY_IF_CHANGED(cloneOriginID); + _lastEdited = usecTimestampNow(); } From e273a0c1a6b8c05b5948faaa57fc30620665a12d Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Tue, 22 May 2018 09:10:50 -0700 Subject: [PATCH 26/29] fix define properties --- libraries/entities/src/EntityItemProperties.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 3286f78917..b616b70d68 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -272,11 +272,11 @@ public: DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); - DEFINE_PROPERTY_REF(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); - DEFINE_PROPERTY_REF(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); - DEFINE_PROPERTY_REF(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); - DEFINE_PROPERTY_REF(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); - DEFINE_PROPERTY_REF(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); + DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE); + DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME); + DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT); + DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); + DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); static QString getComponentModeString(uint32_t mode); From 7efcc135e4b99350782f72dda741f25bbe8dd361 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Tue, 22 May 2018 11:03:41 -0700 Subject: [PATCH 27/29] reorder flags --- libraries/entities/src/EntityItem.cpp | 8 ++++---- libraries/entities/src/EntityItem.h | 4 ++-- libraries/entities/src/EntityPropertyFlags.h | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9bad6f2519..645c310062 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -300,7 +300,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit()); APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic()); APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -867,7 +867,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit); READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); - READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); + READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); @@ -3061,7 +3061,7 @@ bool EntityItem::getCloneDynamic() const { return result; } -void EntityItem::setCloneDynamic(const bool value) { +void EntityItem::setCloneDynamic(bool value) { withWriteLock([&] { _cloneDynamic = value; }); @@ -3075,7 +3075,7 @@ bool EntityItem::getCloneAvatarEntity() const { return result; } -void EntityItem::setCloneAvatarEntity(const bool value) { +void EntityItem::setCloneAvatarEntity(bool value) { withWriteLock([&] { _cloneAvatarEntity = value; }); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 42c4605e44..edd4dcbd34 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -348,9 +348,9 @@ public: float getCloneLimit() const; void setCloneLimit(float value); bool getCloneDynamic() const; - void setCloneDynamic(const bool value); + void setCloneDynamic(bool value); bool getCloneAvatarEntity() const; - void setCloneAvatarEntity(const bool value); + void setCloneAvatarEntity(bool value); const QUuid getCloneOriginID() const; void setCloneOriginID(const QUuid& value); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 85ab28afdc..2fdcd62949 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -204,6 +204,13 @@ enum EntityPropertyList { PROP_CERTIFICATE_ID, PROP_STATIC_CERTIFICATE_VERSION, + PROP_CLONEABLE, + PROP_CLONE_LIFETIME, + PROP_CLONE_LIMIT, + PROP_CLONE_DYNAMIC, + PROP_CLONE_AVATAR_ENTITY, + PROP_CLONE_ORIGIN_ID, + PROP_HAZE_MODE, PROP_KEYLIGHT_COLOR, @@ -242,13 +249,6 @@ enum EntityPropertyList { PROP_MATERIAL_MAPPING_ROT, PROP_MATERIAL_DATA, - PROP_CLONEABLE, - PROP_CLONE_LIFETIME, - PROP_CLONE_LIMIT, - PROP_CLONE_DYNAMIC, - PROP_CLONE_AVATAR_ENTITY, - PROP_CLONE_ORIGIN_ID, - //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, From 0ebd13b3e6b4a088487d908dc285c306328cd38f Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Tue, 22 May 2018 13:17:23 -0700 Subject: [PATCH 28/29] cannot edit origin from client / fix ICE issue --- libraries/entities/src/EntityItemProperties.cpp | 2 -- libraries/networking/src/udt/PacketHeaders.h | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index e9179d8adc..30e2b13daf 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2372,7 +2372,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit()); APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic()); APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, properties.getCloneOriginID()); } if (propertyCount > 0) { @@ -2748,7 +2747,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIMIT, float, setCloneLimit); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); return valid; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 5cb2d49a53..d25806b108 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -75,7 +75,6 @@ public: EntityData, EntityQuery, EntityAdd, - EntityClone, EntityErase, EntityEdit, DomainServerConnectionToken, @@ -132,6 +131,9 @@ public: OctreeDataFileReply, OctreeDataPersist, + EntityClone, + + NUM_PACKET_TYPE }; From 56493346c1e2e1c8068e63cfddf18ea2142b6c11 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Tue, 22 May 2018 15:41:48 -0700 Subject: [PATCH 29/29] fix permissions check --- libraries/entities/src/EntityScriptingInterface.cpp | 7 +++---- libraries/entities/src/EntityScriptingInterface.h | 2 +- libraries/entities/src/EntityTree.cpp | 4 ++-- libraries/entities/src/EntityTree.h | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 - 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f1eb3862a1..f751a4f8ed 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -271,14 +271,13 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties } } -bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properties, EntityItemID& id) { +bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properties, EntityItemID& id, bool isClone) { bool success = true; - id = EntityItemID(QUuid::createUuid()); if (_entityTree) { _entityTree->withWriteLock([&] { - EntityItemPointer entity = _entityTree->addEntity(id, properties); + EntityItemPointer entity = _entityTree->addEntity(id, properties, isClone); if (entity) { if (properties.queryAACubeRelatedPropertyChanged()) { // due to parenting, the server may not know where something is in world-space, so include the bounding cube. @@ -340,7 +339,7 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { // setLastEdited timestamp to 0 to ensure this entity gets updated with the properties // from the server-created entity, don't change this unless you know what you are doing properties.setLastEdited(0); - bool success = addLocalEntityCopy(properties, newEntityID); + bool success = addLocalEntityCopy(properties, newEntityID, true); if (success) { getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); return newEntityID; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index febc33ffde..9acbc13251 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1885,7 +1885,7 @@ private: bool polyVoxWorker(QUuid entityID, std::function actor); bool setPoints(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); - bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id); + bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id, bool isClone = false); EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID, EntityTypes::EntityType entityType = EntityTypes::Unknown); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index dff51a189b..3d96107071 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -493,7 +493,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti return true; } -EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { +EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone) { EntityItemPointer result = NULL; EntityItemProperties props = properties; @@ -505,7 +505,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti if (!properties.getClientOnly() && getIsClient() && !nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() && - !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain) { + !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) { return nullptr; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index aa6e382112..d5097b9a12 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -110,7 +110,7 @@ public: // The newer API... void postAddEntity(EntityItemPointer entityItem); - EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties); + EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false); // use this method if you only know the entityID bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr)); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d25806b108..29d4efed42 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -133,7 +133,6 @@ public: EntityClone, - NUM_PACKET_TYPE };