();
+ 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",
});
});