diff --git a/BUILD_WIN.md b/BUILD_WIN.md index a81fca5900..01ba2887a3 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -53,6 +53,10 @@ Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio. Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance. +Create another environment variable (see Step #4) +* Set "Variable name": `PreferredToolArchitecture` +* Set "Variable value": `x64` + Run from the menu bar `Build > Build Solution`. ### Step 7. Testing Interface diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index a6542689e0..2b964ae674 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -372,7 +372,7 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream // Record explicitly filtered-in entity so that extra entities can be flagged. entityNodeData->insertSentFilteredEntity(entityID); } - OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData); + OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData, entityNode->getCanGetAndSetPrivateUserData()); if (appendEntityState != OctreeElement::COMPLETED) { if (appendEntityState == OctreeElement::PARTIAL) { diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 74c744aced..488b2091f3 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 2.2, + "version": 2.3, "settings": [ { "name": "metaverse", @@ -224,7 +224,7 @@ "name": "standard_permissions", "type": "table", "label": "Domain-Wide User Permissions", - "help": "Indicate which types of users can have which domain-wide permissions.", + "help": "Indicate which types of users can have which domain-wide permissions.", "caption": "Standard Permissions", "can_add_new_rows": false, "groups": [ @@ -233,8 +233,8 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 11 } ], "columns": [ @@ -311,6 +311,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false } ], "non-deletable-row-key": "permissions_id", @@ -337,6 +344,7 @@ "id_can_rez_tmp": true, "id_can_rez_tmp_certified": true, "id_can_write_to_asset_server": true, + "id_can_get_and_set_private_user_data": true, "permissions_id": "localhost" }, { @@ -361,8 +369,8 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 11 } ], "columns": [ @@ -464,6 +472,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -482,8 +497,8 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 11 } ], "columns": [ @@ -582,6 +597,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -596,8 +618,8 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 11 } ], "columns": [ @@ -674,6 +696,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -688,8 +717,8 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 11 } ], "columns": [ @@ -766,6 +795,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false } ] }, @@ -780,8 +816,8 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 11 } ], "columns": [ @@ -858,7 +894,14 @@ "type": "checkbox", "editable": true, "default": false - } + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false + } ] }, { @@ -872,8 +915,8 @@ "span": 1 }, { - "label": "Permissions ?", - "span": 10 + "label": "Permissions ?", + "span": 11 } ], "columns": [ @@ -950,6 +993,13 @@ "type": "checkbox", "editable": true, "default": false + }, + { + "name": "id_can_get_and_set_private_user_data", + "label": "Can Get and Set Private User Data", + "type": "checkbox", + "editable": true, + "default": false } ] }, diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index e53196c67f..29656f4465 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -282,6 +282,7 @@ void DomainGatekeeper::updateNodePermissions() { userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities; userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer; userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent; + userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData; } else { // at this point we don't have a sending socket for packets from this node - assume it is the active socket // or the public socket if we haven't activated a socket for the node yet @@ -374,6 +375,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities; userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer; userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent; + userPerms.permissions |= NodePermissions::Permission::canGetAndSetPrivateUserData; newNode->setPermissions(userPerms); return newNode; } diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 4e833f6b77..2deb51f2e3 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -441,6 +441,12 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena } } + if (oldVersion < 2.3) { + unpackPermissions(); + _standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canGetAndSetPrivateUserData); + packPermissions(); + } + // write the current description version to our settings *versionVariant = _descriptionVersion; diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index 5959725a6a..87db317b17 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -85,23 +85,11 @@ TabBar { NewEntityButton { icon: "icons/create-icons/21-cube-01.svg" - text: "CUBE" + text: "SHAPE" onClicked: { editRoot.sendToScript({ method: "newEntityButtonClicked", - params: { buttonName: "newCubeButton" } - }); - editTabView.currentIndex = 2 - } - } - - NewEntityButton { - icon: "icons/create-icons/22-sphere-01.svg" - text: "SPHERE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", - params: { buttonName: "newSphereButton" } + params: { buttonName: "newShapeButton" } }); editTabView.currentIndex = 2 } diff --git a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml index 6b64520feb..1f062815b6 100644 --- a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml @@ -91,23 +91,11 @@ TabBar { NewEntityButton { icon: "icons/create-icons/21-cube-01.svg" - text: "CUBE" + text: "SHAPE" onClicked: { editRoot.sendToScript({ method: "newEntityButtonClicked", - params: { buttonName: "newCubeButton" } - }); - editTabView.currentIndex = tabIndex.properties - } - } - - NewEntityButton { - icon: "icons/create-icons/22-sphere-01.svg" - text: "SPHERE" - onClicked: { - editRoot.sendToScript({ - method: "newEntityButtonClicked", - params: { buttonName: "newSphereButton" } + params: { buttonName: "newShapeButton" } }); editTabView.currentIndex = tabIndex.properties } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 0a9a076efb..bf0abe92dd 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -361,6 +361,12 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { const uint32_t QUAD_STRIDE = 4; ShapeType type = getShapeType(); + + auto model = getModel(); + if (!model) { + type = SHAPE_TYPE_NONE; + } + if (type == SHAPE_TYPE_COMPOUND) { updateModelBounds(); @@ -442,10 +448,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - auto model = getModel(); - // assert we never fall in here when model not fully loaded - assert(model && model->isLoaded()); - glm::vec3 dimensions = getScaledDimensions(); glm::vec3 scaleToFit = dimensions / model->getHFMModel().getUnscaledMeshExtents().size(); // multiply each point by scale before handing the point-set off to the physics engine. @@ -461,7 +463,6 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { adjustShapeInfoByRegistration(shapeInfo); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { updateModelBounds(); - auto model = getModel(); // assert we never fall in here when model not fully loaded assert(model && model->isLoaded()); model->updateGeometry(); diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 0491bdedae..dbb3ab076e 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -110,6 +110,10 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityPropertyFlags requestedProperties = propertiesCopy.getChangedProperties(); + if (!nodeList->getThisNodeCanGetAndSetPrivateUserData() && requestedProperties.getHasProperty(PROP_PRIVATE_USER_DATA)) { + requestedProperties -= PROP_PRIVATE_USER_DATA; + } + while (encodeResult == OctreeElement::PARTIAL) { encodeResult = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut, requestedProperties, didntFitProperties); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4232e9d1b9..d39924dcd3 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -86,6 +86,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_NAME; requestedProperties += PROP_LOCKED; requestedProperties += PROP_USER_DATA; + requestedProperties += PROP_PRIVATE_USER_DATA; requestedProperties += PROP_HREF; requestedProperties += PROP_DESCRIPTION; requestedProperties += PROP_POSITION; @@ -154,7 +155,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param } OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData) const { + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + const bool destinationNodeCanGetAndSetPrivateUserData) const { // ALL this fits... // object ID [16 bytes] @@ -198,6 +200,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID()); } + QString privateUserData = ""; + if (destinationNodeCanGetAndSetPrivateUserData) { + privateUserData = getPrivateUserData(); + } + EntityPropertyFlags propertiesDidntFit = requestedProperties; LevelDetails entityLevel = packetData->startLevel(); @@ -266,8 +273,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, _simulationOwner.toByteArray()); // convert AVATAR_SELF_ID to actual sessionUUID. QUuid actualParentID = getParentID(); + auto nodeList = DependencyManager::get(); if (actualParentID == AVATAR_SELF_ID) { - auto nodeList = DependencyManager::get(); actualParentID = nodeList->getSessionUUID(); } APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, actualParentID); @@ -276,6 +283,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); + APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, privateUserData); APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); APPEND_ENTITY_PROPERTY(PROP_POSITION, getLocalPosition()); @@ -812,6 +820,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); + READ_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData); READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription); { // When we own the simulation we don't accept updates to the entity's transform/velocities @@ -1331,6 +1340,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked); COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(privateUserData, getPrivateUserData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription); COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getLocalPosition); @@ -1479,6 +1489,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(privateUserData, setPrivateUserData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPosition); @@ -3121,6 +3132,20 @@ void EntityItem::setUserData(const QString& value) { }); } +QString EntityItem::getPrivateUserData() const { + QString result; + withReadLock([&] { + result = _privateUserData; + }); + return result; +} + +void EntityItem::setPrivateUserData(const QString& value) { + withWriteLock([&] { + _privateUserData = value; + }); +} + // Certifiable Properties #define DEFINE_PROPERTY_GETTER(type, accessor, var) \ type EntityItem::get##accessor() const { \ diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a8c3d31344..424454f528 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -134,7 +134,8 @@ public: virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData) const; + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + const bool destinationNodeCanGetAndSetPrivateUserData = false) const; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, @@ -332,6 +333,9 @@ public: QString getUserData() const; virtual void setUserData(const QString& value); // FIXME: This is suspicious + QString getPrivateUserData() const; + void setPrivateUserData(const QString& value); + // FIXME not thread safe? const SimulationOwner& getSimulationOwner() const { return _simulationOwner; } void setSimulationOwner(const QUuid& id, uint8_t priority); @@ -644,6 +648,7 @@ protected: bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC }; bool _locked { ENTITY_ITEM_DEFAULT_LOCKED }; QString _userData { ENTITY_ITEM_DEFAULT_USER_DATA }; + QString _privateUserData{ ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA }; SimulationOwner _simulationOwner; bool _shouldHighlight { false }; QString _name { ENTITY_ITEM_DEFAULT_NAME }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5d39139b46..ce4387a006 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -487,6 +487,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked); CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData); + CHECK_PROPERTY_CHANGE(PROP_PRIVATE_USER_DATA, privateUserData); CHECK_PROPERTY_CHANGE(PROP_HREF, href); CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description); CHECK_PROPERTY_CHANGE(PROP_POSITION, position); @@ -818,6 +819,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * which you can manipulate the properties of, and use JSON.stringify() to convert the object into a string to * put in the property. * + * @property {string} privateUserData="" - Like userData, but only accessible by Entity Server Scripts, AC scripts, and users + * who are given "Can Get and Set Private User Data" permissions from the ACL matrix on the Domain Settings page. + * * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. * @property {number} scriptTimestamp=0 - Intended to be used to indicate when the client entity script was loaded. Should be * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). @@ -1591,6 +1595,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PRIVATE_USER_DATA, privateUserData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, position); @@ -1999,6 +2004,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); COPY_PROPERTY_FROM_QSCRIPTVALUE(locked, bool, setLocked); COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData); + COPY_PROPERTY_FROM_QSCRIPTVALUE(privateUserData, QString, setPrivateUserData); COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref); COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription); COPY_PROPERTY_FROM_QSCRIPTVALUE(position, vec3, setPosition); @@ -2288,6 +2294,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(name); COPY_PROPERTY_IF_CHANGED(locked); COPY_PROPERTY_IF_CHANGED(userData); + COPY_PROPERTY_IF_CHANGED(privateUserData); COPY_PROPERTY_IF_CHANGED(href); COPY_PROPERTY_IF_CHANGED(description); COPY_PROPERTY_IF_CHANGED(position); @@ -2573,6 +2580,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); ADD_PROPERTY_TO_MAP(PROP_LOCKED, Locked, locked, bool); ADD_PROPERTY_TO_MAP(PROP_USER_DATA, UserData, userData, QString); + ADD_PROPERTY_TO_MAP(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString); ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); @@ -3047,6 +3055,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData()); + APPEND_ENTITY_PROPERTY(PROP_PRIVATE_USER_DATA, properties.getPrivateUserData()); APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition()); @@ -3530,6 +3539,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PRIVATE_USER_DATA, QString, setPrivateUserData); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, vec3, setPosition); @@ -3941,6 +3951,7 @@ void EntityItemProperties::markAllChanged() { _nameChanged = true; _lockedChanged = true; _userDataChanged = true; + _privateUserDataChanged = true; _hrefChanged = true; _descriptionChanged = true; _positionChanged = true; @@ -4303,6 +4314,9 @@ QList EntityItemProperties::listChangedProperties() { if (userDataChanged()) { out += "userData"; } + if (privateUserDataChanged()) { + out += "privateUserData"; + } if (hrefChanged()) { out += "href"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 0142f42536..af945a89de 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -180,6 +180,7 @@ public: DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED); DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA); + DEFINE_PROPERTY_REF(PROP_PRIVATE_USER_DATA, PrivateUserData, privateUserData, QString, ENTITY_ITEM_DEFAULT_PRIVATE_USER_DATA); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3); @@ -607,6 +608,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, PrivateUserData, privateUserData, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SimulationOwner, simulationOwner, SimulationOwner()); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index be3c566724..cc12eb37b4 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -28,6 +28,7 @@ const QVector ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC = QVectorgetPacketReceiver(); packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket"); @@ -107,6 +108,11 @@ bool EntityScriptingInterface::canReplaceContent() { return nodeList->getThisNodeCanReplaceContent(); } +bool EntityScriptingInterface::canGetAndSetPrivateUserData() { + auto nodeList = DependencyManager::get(); + return nodeList->getThisNodeCanGetAndSetPrivateUserData(); +} + void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { if (_entityTree) { disconnect(_entityTree.get(), &EntityTree::addingEntityPointer, this, &EntityScriptingInterface::onAddingEntity); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 38b73aaf45..c072dedaf9 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -236,6 +236,14 @@ public slots: */ Q_INVOKABLE bool canReplaceContent(); + /**jsdoc + * Check whether or not you can get and set private user data. + * @function Entities.canGetAndSetPrivateUserData + * @returns {boolean} true if the domain server will allow the user to get and set private user data, + * otherwise false. + */ + Q_INVOKABLE bool canGetAndSetPrivateUserData(); + /**jsdoc *

How an entity is sent over the wire.

* @@ -1861,6 +1869,15 @@ signals: */ void canWriteAssetsChanged(bool canWriteAssets); + /**jsdoc + * Triggered when your ability to get and set private user data changes. + * @function Entities.canGetAndSetPrivateUserDataChanged + * @param {boolean} canGetAndSetPrivateUserData - true if you can change the privateUserData property of an entity, + * otherwise false. + * @returns {Signal} + */ + void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData); + /**jsdoc * Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index fe8c8c9336..652266bd48 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1286,6 +1286,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { + QString changeHint = properties.getPrivateUserData(); + changedProperties[index] = QString("privateUserData:") + changeHint; + } + } + if (properties.parentJointIndexChanged()) { int index = changedProperties.indexOf("parentJointIndex"); if (index >= 0) { @@ -1772,6 +1780,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c bool suppressDisallowedClientScript = false; bool suppressDisallowedServerScript = false; + bool suppressDisallowedPrivateUserData = false; bool isPhysics = message.getType() == PacketType::EntityPhysics; _totalEditMessages++; @@ -1860,7 +1869,22 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } } + } + if (!properties.getPrivateUserData().isEmpty() && validEditPacket && !senderNode->getCanGetAndSetPrivateUserData()) { + if (wantEditLogging()) { + qCDebug(entities) << "User [" << senderNode->getUUID() + << "] is attempting to set private user data but user isn't allowed; edit rejected..."; + } + + // If this was an add, we also want to tell the client that sent this edit that the entity was not added. + if (isAdd) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + validEditPacket = false; + } else { + suppressDisallowedPrivateUserData = true; + } } if (!isClone) { @@ -1915,6 +1939,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c properties.setServerScripts(existingEntity->getServerScripts()); } + if (suppressDisallowedPrivateUserData) { + bumpTimestamp(properties); + properties.setPrivateUserData(existingEntity->getPrivateUserData()); + } + // if the EntityItem exists, then update it startLogging = usecTimestampNow(); if (wantEditLogging()) { diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index cb9637acd5..f276bea05d 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -294,9 +294,7 @@ void ModelEntityItem::setModelURL(const QString& url) { withWriteLock([&] { if (_modelURL != url) { _modelURL = url; - if (_shapeType == SHAPE_TYPE_STATIC_MESH) { - _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; - } + _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } }); } @@ -329,11 +327,8 @@ const Transform ModelEntityItem::getTransform(bool& success, int depth) const { void ModelEntityItem::setCompoundShapeURL(const QString& url) { withWriteLock([&] { if (_compoundShapeURL.get() != url) { - ShapeType oldType = computeTrueShapeType(); _compoundShapeURL.set(url); - if (oldType != computeTrueShapeType()) { - _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; - } + _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } }); } diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index d87d74c188..563d6e7ad1 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -197,6 +197,10 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) { newPermissions.can(NodePermissions::Permission::canReplaceDomainContent)) { emit canReplaceContentChanged(_permissions.can(NodePermissions::Permission::canReplaceDomainContent)); } + if (originalPermissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData) != + newPermissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData)) { + emit canGetAndSetPrivateUserDataChanged(_permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData)); + } } void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 4db4f3136a..60c91c8ff6 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -124,6 +124,7 @@ public: bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); } + bool getThisNodeCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); } quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); } Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort); @@ -368,6 +369,7 @@ signals: void canWriteAssetsChanged(bool canWriteAssets); void canKickChanged(bool canKick); void canReplaceContentChanged(bool canReplaceContent); + void canGetAndSetPrivateUserDataChanged(bool canGetAndSetPrivateUserData); protected slots: void connectedForLocalSocketTest(); diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index fe3177d785..07c599913b 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -83,6 +83,7 @@ public: bool getCanWriteToAssetServer() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); } bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); } bool getCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); } + bool getCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); } using NodesIgnoredPair = std::pair, bool>; diff --git a/libraries/networking/src/NodePermissions.cpp b/libraries/networking/src/NodePermissions.cpp index 92ebf1d01e..e0de649c05 100644 --- a/libraries/networking/src/NodePermissions.cpp +++ b/libraries/networking/src/NodePermissions.cpp @@ -67,6 +67,7 @@ NodePermissions::NodePermissions(QMap perms) { Permission::canConnectPastMaxCapacity : Permission::none; permissions |= perms["id_can_kick"].toBool() ? Permission::canKick : Permission::none; permissions |= perms["id_can_replace_content"].toBool() ? Permission::canReplaceDomainContent : Permission::none; + permissions |= perms["id_can_get_and_set_private_user_data"].toBool() ? Permission::canGetAndSetPrivateUserData : Permission::none; } QVariant NodePermissions::toVariant(QHash groupRanks) { @@ -94,6 +95,7 @@ QVariant NodePermissions::toVariant(QHash groupRanks) { values["id_can_connect_past_max_capacity"] = can(Permission::canConnectPastMaxCapacity); values["id_can_kick"] = can(Permission::canKick); values["id_can_replace_content"] = can(Permission::canReplaceDomainContent); + values["id_can_get_and_set_private_user_data"] = can(Permission::canGetAndSetPrivateUserData); return QVariant(values); } @@ -166,6 +168,9 @@ QDebug operator<<(QDebug debug, const NodePermissions& perms) { if (perms.can(NodePermissions::Permission::canReplaceDomainContent)) { debug << " can_replace_content"; } + if (perms.can(NodePermissions::Permission::canGetAndSetPrivateUserData)) { + debug << " get-and-set-private-user-data"; + } debug.nospace() << "]"; return debug.nospace(); } diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 1b0b9d220d..583c1b29ac 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -75,7 +75,8 @@ public: canKick = 64, canReplaceDomainContent = 128, canRezPermanentCertifiedEntities = 256, - canRezTemporaryCertifiedEntities = 512 + canRezTemporaryCertifiedEntities = 512, + canGetAndSetPrivateUserData = 1024 }; Q_DECLARE_FLAGS(Permissions, Permission) Permissions permissions; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index ed81a6d9ca..5deadd8c43 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -270,6 +270,7 @@ enum class EntityVersion : PacketVersion { DisableWebMedia, ParticleShapeType, ParticleShapeTypeDeadlockFix, + PrivateUserData, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index daa2b5d954..3c730fc6cf 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -249,12 +249,19 @@ void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() { btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShapeByKey(requestItr->shapeHash)); if (shape) { // shape is ready at last! - // But the entity's desired shape might have changed since last requested - // --> rebuild the ShapeInfo to verify hash + // but the entity's physics desired physics status may have changed since last requested + if (!entity->shouldBePhysical()) { + requestItr = _shapeRequests.erase(requestItr); + continue; + } + // rebuild the ShapeInfo to verify hash because entity's desired shape may have changed // TODO? is there a better way to do this? ShapeInfo shapeInfo; entity->computeShapeInfo(shapeInfo); - if (shapeInfo.getHash() != requestItr->shapeHash) { + + if (shapeInfo.getType() == SHAPE_TYPE_NONE) { + requestItr = _shapeRequests.erase(requestItr); + } else if (shapeInfo.getHash() != requestItr->shapeHash) { // bummer, the hashes are different and we no longer want the shape we've received ObjectMotionState::getShapeManager()->releaseShape(shape); // try again diff --git a/scripts/developer/tests/toolbarTest.js b/scripts/developer/tests/toolbarTest.js index b713445927..89609e610d 100644 --- a/scripts/developer/tests/toolbarTest.js +++ b/scripts/developer/tests/toolbarTest.js @@ -5,8 +5,7 @@ var toolBar = (function() { toolBar, activeButton, newModelButton, - newCubeButton, - newSphereButton, + newShapeButton, newLightButton, newTextButton, newWebButton, @@ -41,20 +40,13 @@ var toolBar = (function() { visible: false }); - newCubeButton = toolBar.addButton({ - objectName: "newCubeButton", + newShapeButton = toolBar.addButton({ + objectName: "newShapeButton", imageURL: toolIconUrl + "cube-01.svg", alpha: 0.9, visible: false }); - newSphereButton = toolBar.addButton({ - objectName: "newSphereButton", - imageURL: toolIconUrl + "sphere-01.svg", - alpha: 0.9, - visible: false - }); - newLightButton = toolBar.addButton({ objectName: "newLightButton", imageURL: toolIconUrl + "light-01.svg", @@ -111,8 +103,7 @@ var toolBar = (function() { // Sets visibility of tool buttons, excluding the power button that.showTools = function(doShow) { newModelButton.writeProperty('visible', doShow); - newCubeButton.writeProperty('visible', doShow); - newSphereButton.writeProperty('visible', doShow); + newShapeButton.writeProperty('visible', doShow); newLightButton.writeProperty('visible', doShow); newTextButton.writeProperty('visible', doShow); newWebButton.writeProperty('visible', doShow); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 69cf278ab3..371a8d48ca 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -860,15 +860,10 @@ var toolBar = (function () { addButton("newModelButton", createNewEntityDialogButtonCallback("Model")); - addButton("newCubeButton", function () { + addButton("newShapeButton", function () { createNewEntity({ - type: "Box", - }); - }); - - addButton("newSphereButton", function () { - createNewEntity({ - type: "Sphere", + type: "Shape", + shape: "Cube", }); });