From 079d1dcbd694bd498d59a226b1ca550123467891 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 12 Nov 2018 18:00:28 -0800 Subject: [PATCH 01/22] convert clientOnly to entityHost and add local entities --- interface/src/AvatarBookmarks.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 5 +- .../scripting/WalletScriptingInterface.cpp | 2 +- .../ui/overlays/ContextOverlayInterface.cpp | 4 +- .../src/avatars-renderer/Avatar.cpp | 4 +- .../src/RenderableEntityItem.cpp | 13 +-- .../entities/src/EntityEditPacketSender.cpp | 9 ++- libraries/entities/src/EntityItem.cpp | 17 ++-- libraries/entities/src/EntityItem.h | 18 ++++- .../entities/src/EntityItemProperties.cpp | 79 +++++++++++++------ libraries/entities/src/EntityItemProperties.h | 4 +- .../entities/src/EntityPropertyFlags.cpp | 2 +- libraries/entities/src/EntityPropertyFlags.h | 2 +- .../entities/src/EntityPsuedoPropertyFlags.h | 2 + .../entities/src/EntityScriptingInterface.cpp | 31 +++++--- .../entities/src/EntityScriptingInterface.h | 39 +++++++-- libraries/entities/src/EntityTree.cpp | 12 ++- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/physics/src/EntityMotionState.cpp | 10 +-- .../physics/src/PhysicalEntitySimulation.cpp | 4 +- scripts/system/attachedEntitiesManager.js | 2 +- scripts/system/avatarapp.js | 8 +- scripts/system/libraries/WebTablet.js | 3 +- .../system/libraries/entitySelectionTool.js | 2 +- scripts/system/marketplaces/marketplace.js | 2 +- scripts/system/nameTag.js | 4 +- scripts/system/tablet-ui/tabletUI.js | 2 +- 28 files changed, 187 insertions(+), 100 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index ce21c68c3d..df7620013c 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -58,7 +58,7 @@ void addAvatarEntities(const QVariantList& avatarEntities) { EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties); entityProperties.setParentID(myNodeID); - entityProperties.setClientOnly(true); + entityProperties.setEntityHost(EntityHost::AVATAR_ENTITY); entityProperties.setOwningAvatarID(myNodeID); entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY); entityProperties.markAllChanged(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 876e64c592..ff6c7e4710 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -911,8 +911,7 @@ void MyAvatar::simulate(float deltaTime) { moveOperator.addEntityToMoveList(entity, newCube); } // send an edit packet to update the entity-server about the queryAABox - // unless it is client-only - if (packetSender && !entity->getClientOnly()) { + if (packetSender && entity->isDomainEntity()) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); properties.setLastEdited(now); @@ -923,7 +922,7 @@ void MyAvatar::simulate(float deltaTime) { entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { EntityItemPointer entityDescendant = std::dynamic_pointer_cast(descendant); - if (entityDescendant && !entityDescendant->getClientOnly() && descendant->updateQueryAACube()) { + if (entityDescendant && entityDescendant->isDomainEntity() && descendant->updateQueryAACube()) { EntityItemProperties descendantProperties; descendantProperties.setQueryAACube(descendant->getQueryAACube()); descendantProperties.setLastEdited(now); diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 93ee60ba5b..6ad81f0e2b 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -35,7 +35,7 @@ void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUui QSharedPointer contextOverlayInterface = DependencyManager::get(); EntityItemProperties entityProperties = DependencyManager::get()->getEntityProperties(entityID, contextOverlayInterface->getEntityPropertyFlags()); - if (entityProperties.getClientOnly()) { + if (entityProperties.getEntityHost() == EntityHost::AVATAR_ENTITY) { if (!entityID.isNull() && entityProperties.getCertificateID().length() > 0) { contextOverlayInterface->requestOwnershipVerification(entityID); } else { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 11c268dd48..f6c213163d 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -46,7 +46,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; _entityPropertyFlags += PROP_CERTIFICATE_ID; - _entityPropertyFlags += PROP_CLIENT_ONLY; + _entityPropertyFlags += PROP_ENTITY_HOST; _entityPropertyFlags += PROP_OWNING_AVATAR_ID; auto entityScriptingInterface = DependencyManager::get().data(); @@ -296,7 +296,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID auto nodeList = DependencyManager::get(); if (entityProperties.verifyStaticCertificateProperties()) { - if (entityProperties.getClientOnly()) { + if (entityProperties.getEntityHost() == EntityHost::AVATAR_ENTITY) { SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); if (entityServer) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index f694148b30..fcac61a0bd 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -310,7 +310,7 @@ void Avatar::updateAvatarEntities() { PerformanceTimer perfTimer("attachments"); // AVATAR ENTITY UPDATE FLOW - // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity() + // - if queueEditEntityMessage sees avatarEntity flag it does _myAvatar->updateAvatarEntity() // - updateAvatarEntity saves the bytes and flags the trait instance for the entity as updated // - ClientTraitsHandler::sendChangedTraitsToMixer sends the entity bytes to the mixer which relays them to other interfaces // - AvatarHashMap::processBulkAvatarTraits on other interfaces calls avatar->processTraitInstace @@ -389,7 +389,7 @@ void Avatar::updateAvatarEntities() { QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); EntityItemProperties properties; EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, properties); - properties.setClientOnly(true); + properties.setEntityHost(EntityHost::AVATAR_ENTITY); properties.setOwningAvatarID(getID()); // there's no entity-server to tell us we're the simulation owner, so always set the diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index aa335bb7d5..9b9d0bb7a9 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -39,7 +39,7 @@ enum class RenderItemStatusIcon { SIMULATION_OWNER = 3, HAS_ACTIONS = 4, OTHER_SIMULATION_OWNER = 5, - CLIENT_ONLY = 6, + ENTITY_HOST = 6, NONE = 255 }; @@ -115,17 +115,20 @@ void EntityRenderer::makeStatusGetters(const EntityItemPointer& entity, Item::St }); statusGetters.push_back([entity, myNodeID] () -> render::Item::Status::Value { - if (entity->getClientOnly()) { + if (entity->isAvatarEntity()) { if (entity->getOwningAvatarID() == myNodeID) { return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN, - (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + (unsigned char)RenderItemStatusIcon::ENTITY_HOST); } else { return render::Item::Status::Value(1.0f, render::Item::Status::Value::RED, - (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + (unsigned char)RenderItemStatusIcon::ENTITY_HOST); } + } else if (entity->isLocalEntity()) { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE, + (unsigned char)RenderItemStatusIcon::ENTITY_HOST); } return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN, - (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + (unsigned char)RenderItemStatusIcon::ENTITY_HOST); }); } diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 4aa66db227..0169bb6d9c 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -84,16 +84,19 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityTreePointer entityTree, EntityItemID entityItemID, const EntityItemProperties& properties) { - if (properties.getClientOnly()) { + if (properties.getEntityHost() == EntityHost::AVATAR_ENTITY) { if (!_myAvatar) { - qCWarning(entities) << "Suppressing entity edit message: cannot send clientOnly edit with no myAvatar"; + qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit with no myAvatar"; } else if (properties.getOwningAvatarID() == _myAvatar->getID()) { // this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties); } else { - qCWarning(entities) << "Suppressing entity edit message: cannot send clientOnly edit for another avatar"; + qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit for another avatar"; } return; + } else if (properties.getEntityHost() == EntityHost::LOCAL_ENTITY) { + // Don't send edits for local entities + return; } if (entityTree && entityTree->isServerlessMode()) { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 36ffc68fd3..8ffde04b23 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -119,7 +119,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_PARENT_JOINT_INDEX; requestedProperties += PROP_QUERY_AA_CUBE; - requestedProperties += PROP_CLIENT_ONLY; + requestedProperties += PROP_ENTITY_HOST; requestedProperties += PROP_OWNING_AVATAR_ID; requestedProperties += PROP_LAST_EDITED_BY; @@ -172,7 +172,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); - requestedProperties -= PROP_CLIENT_ONLY; + requestedProperties -= PROP_ENTITY_HOST; requestedProperties -= PROP_OWNING_AVATAR_ID; // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, @@ -1278,10 +1278,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire EntityItemProperties properties(propertyFlags); properties._id = getID(); properties._idSet = true; - properties._created = getCreated(); properties._lastEdited = getLastEdited(); - properties.setClientOnly(getClientOnly()); - properties.setOwningAvatarID(getOwningAvatarID()); properties._type = getType(); @@ -1337,7 +1334,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(localPosition, getLocalPosition); COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRotation, getLocalOrientation); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(clientOnly, getClientOnly); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityHost, getEntityHost); COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy); @@ -1479,7 +1476,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(clientOnly, setClientOnly); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityHost, setEntityHost); SET_ENTITY_PROPERTY_FROM_PROPERTIES(owningAvatarID, setOwningAvatarID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); @@ -2496,7 +2493,7 @@ void EntityItem::dimensionsChanged() { bool EntityItem::getScalesWithParent() const { // keep this logic the same as in EntityItemProperties::getScalesWithParent - if (getClientOnly()) { + if (isAvatarEntity()) { QUuid ancestorID = findAncestorOfType(NestableType::Avatar); return !ancestorID.isNull(); } else { @@ -3277,7 +3274,7 @@ void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properti properties.setSimulationOwner(Physics::getSessionUUID(), priority); setPendingOwnershipPriority(priority); - properties.setClientOnly(getClientOnly()); + properties.setEntityHost(getEntityHost()); properties.setOwningAvatarID(getOwningAvatarID()); setLastBroadcast(now); // for debug/physics status icons -} +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c49017b2e0..e119595eb6 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -64,6 +64,12 @@ const uint64_t MAX_INCOMING_SIMULATION_UPDATE_PERIOD = MAX_OUTGOING_SIMULATION_U class MeshProxyList; +enum class EntityHost { + DOMAIN_ENTITY = 0, + AVATAR_ENTITY, + LOCAL_ENTITY +}; + /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate /// one directly, instead you must only construct one of it's derived classes with additional features. @@ -478,9 +484,13 @@ public: void setScriptHasFinishedPreload(bool value); bool isScriptPreloadFinished(); - bool getClientOnly() const { return _clientOnly; } - virtual void setClientOnly(bool clientOnly) { _clientOnly = clientOnly; } - // if this entity is client-only, which avatar is it associated with? + bool isDomainEntity() const { return _entityHost == EntityHost::DOMAIN_ENTITY; } + bool isAvatarEntity() const { return _entityHost == EntityHost::AVATAR_ENTITY; } + bool isLocalEntity() const { return _entityHost == EntityHost::LOCAL_ENTITY; } + EntityHost getEntityHost() const { return _entityHost; } + virtual void setEntityHost(EntityHost entityHost) { _entityHost = entityHost; } + + // if this entity is an avatar entity, which avatar is it associated with? QUuid getOwningAvatarID() const { return _owningAvatarID; } virtual void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } @@ -673,7 +683,7 @@ protected: QUuid _sourceUUID; /// the server node UUID we came from - bool _clientOnly { false }; + EntityHost _entityHost { EntityHost::DOMAIN_ENTITY }; bool _transitingWithAvatar{ false }; QUuid _owningAvatarID; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c243f772e2..ff8da64f60 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -306,6 +306,29 @@ void EntityItemProperties::setMaterialMappingModeFromString(const QString& mater } } +QString EntityItemProperties::getEntityHostAsString() const { + switch (_entityHost) { + case EntityHost::DOMAIN_ENTITY: + return "domain"; + case EntityHost::AVATAR_ENTITY: + return "avatar"; + case EntityHost::LOCAL_ENTITY: + return "local"; + default: + return ""; + } +} + +void EntityItemProperties::setEntityHostFromString(const QString& entityHost) { + if (entityHost == "domain") { + _entityHost = EntityHost::DOMAIN_ENTITY; + } else if (entityHost == "avatar") { + _entityHost = EntityHost::AVATAR_ENTITY; + } else if (entityHost == "local") { + _entityHost = EntityHost::LOCAL_ENTITY; + } +} + EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags changedProperties; @@ -453,7 +476,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL); - CHECK_PROPERTY_CHANGE(PROP_CLIENT_ONLY, clientOnly); + CHECK_PROPERTY_CHANGE(PROP_ENTITY_HOST, entityHost); CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID); CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape); @@ -489,12 +512,18 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.EntityType} type - The entity type. You cannot change the type of an entity after it's created. (Though * its value may switch among "Box", "Shape", and "Sphere" depending on changes to * the shape property set for entities of these types.) Read-only. - * @property {boolean} clientOnly=false - If true then the entity is an avatar entity; otherwise it is a server - * entity. An avatar entity follows you to each domain you visit, rendering at the same world coordinates unless it's - * parented to your avatar. Value cannot be changed after the entity is created.
- * The value can also be set at entity creation by using the clientOnly parameter in + * @property {EntityHost} entityHost="domain" - How this entity will behave, including if and how it is sent to other people. + * The value can only be set at entity creation by using the entityHost parameter in * {@link Entities.addEntity}. - * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if clientOnly is + * @property {boolean} avatarEntity=false - If true then the entity is an avatar entity; An avatar entity follows you to each domain you visit, + * rendering at the same world coordinates unless it's parented to your avatar. Value cannot be changed after the entity is created.
+ * The value can only be set at entity creation by using the entityHost parameter in + * {@link Entities.addEntity}. clientOnly is an alias. + * @property {boolean} localEntity=false - If true then the entity is a local entity; Local entities only render for you and are not sent over the wire. + * Value cannot be changed after the entity is created.
+ * The value can only be set at entity creation by using the entityHost parameter in + * {@link Entities.addEntity}. + * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is * true, otherwise {@link Uuid|Uuid.NULL}. Read-only. * * @property {string} created - The UTC date and time that the entity was created, in ISO 8601 format as @@ -594,7 +623,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that this entity is parented to. Use * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a joint. * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, - * otherwise the same value as position. If the entity is parented to an avatar and is clientOnly + * otherwise the same value as position. If the entity is parented to an avatar and is an avatarEntity * so that it scales with the avatar, this value remains the original local position value while the avatar scale changes. * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, * otherwise the same value as rotation. @@ -602,8 +631,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * otherwise the same value as velocity. * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is * parented, otherwise the same value as position. - * @property {Vec3} localDimensions - The dimensions of the entity. If the entity is parented to an avatar and is - * clientOnly so that it scales with the avatar, this value remains the original dimensions value while the + * @property {Vec3} localDimensions - The dimensions of the entity. If the entity is parented to an avatar and is an + * avatarEntity so that it scales with the avatar, this value remains the original dimensions value while the * avatar scale changes. * * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. @@ -628,7 +657,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {boolean} cloneDynamic=false - If true then clones created from this entity will have their * dynamic property set to true. * @property {boolean} cloneAvatarEntity=false - If true then clones created from this entity will be created as - * avatar entities: their clientOnly property will be set to true. + * avatar entities: their avatarEntity property will be set to true. * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. * * @property {Entities.Grab} grab - The grab-related properties. @@ -739,7 +768,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * overlay's ID. * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity - * by setting the clientOnly parameter in {@link Entities.addEntity} to true. + * by setting the entityHost parameter in {@link Entities.addEntity} to "avatar". * Material entities render as non-scalable spheres if they don't have their parent set. * @typedef {object} Entities.EntityProperties-Material * @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append ?name to the URL, the @@ -1527,8 +1556,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_DIMENSIONS, localDimensions); - 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_GETTER(PROP_ENTITY_HOST, entityHost, getEntityHostAsString()); // 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_CLONE_LIFETIME, cloneLifetime); @@ -1567,12 +1596,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(renderInfo, renderInfo); // Gettable but not settable } - // FIXME: These properties should already have been set above. if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::ClientOnly)) { - properties.setProperty("clientOnly", convertScriptValue(engine, getClientOnly())); + properties.setProperty("clientOnly", convertScriptValue(engine, getEntityHost() == EntityHost::AVATAR_ENTITY)); } - if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::OwningAvatarID)) { - properties.setProperty("owningAvatarID", convertScriptValue(engine, getOwningAvatarID())); + if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::AvatarEntity)) { + properties.setProperty("avatarEntity", convertScriptValue(engine, getEntityHost() == EntityHost::AVATAR_ENTITY)); + } + if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::LocalEntity)) { + properties.setProperty("localEntity", convertScriptValue(engine, getEntityHost() == EntityHost::LOCAL_ENTITY)); } // FIXME - I don't think these properties are supported any more @@ -1761,7 +1792,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(entityHost, EntityHost); COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); @@ -1927,7 +1958,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(ghostingAllowed); COPY_PROPERTY_IF_CHANGED(filterURL); - COPY_PROPERTY_IF_CHANGED(clientOnly); + COPY_PROPERTY_IF_CHANGED(entityHost); COPY_PROPERTY_IF_CHANGED(owningAvatarID); COPY_PROPERTY_IF_CHANGED(dpi); @@ -3213,7 +3244,7 @@ void EntityItemProperties::markAllChanged() { _ghostingAllowedChanged = true; _filterURLChanged = true; - _clientOnlyChanged = true; + _entityHostChanged = true; _owningAvatarIDChanged = true; _dpiChanged = true; @@ -3712,8 +3743,8 @@ QList EntityItemProperties::listChangedProperties() { out += "queryAACube"; } - if (clientOnlyChanged()) { - out += "clientOnly"; + if (entityHostChanged()) { + out += "entityHost"; } if (owningAvatarIDChanged()) { out += "owningAvatarID"; @@ -3788,7 +3819,7 @@ bool EntityItemProperties::getScalesWithParent() const { if (success && parent) { bool avatarAncestor = (parent->getNestableType() == NestableType::Avatar || parent->hasAncestorOfType(NestableType::Avatar)); - scalesWithParent = getClientOnly() && avatarAncestor; + scalesWithParent = getEntityHost() == EntityHost::AVATAR_ENTITY && avatarAncestor; } } return scalesWithParent; @@ -3946,7 +3977,7 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setParentJointIndex(-1); setLifetime(getCloneLifetime()); setDynamic(getCloneDynamic()); - setClientOnly(getCloneAvatarEntity()); + setEntityHost(getCloneAvatarEntity() ? EntityHost::AVATAR_ENTITY : EntityHost::DOMAIN_ENTITY); setCreated(usecTimestampNow()); setLastEdited(usecTimestampNow()); setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c91ccda5aa..9e666bca1b 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -279,7 +279,7 @@ public: DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); - DEFINE_PROPERTY(PROP_CLIENT_ONLY, ClientOnly, clientOnly, bool, false); + DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST, EntityHost, entityHost, EntityHost, EntityHost::DOMAIN_ENTITY); DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI); @@ -589,7 +589,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, GhostingAllowed, ghostingAllowed, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, FilterURL, filterURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ClientOnly, clientOnly, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostAsString, entityHost, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, LastEditedBy, lastEditedBy, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.cpp b/libraries/entities/src/EntityPropertyFlags.cpp index c077b153b8..ff95e40fde 100644 --- a/libraries/entities/src/EntityPropertyFlags.cpp +++ b/libraries/entities/src/EntityPropertyFlags.cpp @@ -121,7 +121,7 @@ QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { result = f.getHasProperty(PROP_FALLOFF_RADIUS) ? result + "falloffRadius " : result; result = f.getHasProperty(PROP_FLYING_ALLOWED) ? result + "flyingAllowed " : result; result = f.getHasProperty(PROP_GHOSTING_ALLOWED) ? result + "ghostingAllowed " : result; - result = f.getHasProperty(PROP_CLIENT_ONLY) ? result + "clientOnly " : result; + result = f.getHasProperty(PROP_ENTITY_HOST) ? result + "entityHost " : result; result = f.getHasProperty(PROP_OWNING_AVATAR_ID) ? result + "owningAvatarID " : result; result = f.getHasProperty(PROP_SHAPE) ? result + "shape " : result; result = f.getHasProperty(PROP_DPI) ? result + "dpi " : result; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index d2f687fbd3..7f1d48acdf 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -176,7 +176,7 @@ enum EntityPropertyList { PROP_FLYING_ALLOWED, // can avatars in a zone fly? PROP_GHOSTING_ALLOWED, // can avatars in a zone turn off physics? - PROP_CLIENT_ONLY, // doesn't go over wire + PROP_ENTITY_HOST, // doesn't go over wire PROP_OWNING_AVATAR_ID, // doesn't go over wire PROP_SHAPE, diff --git a/libraries/entities/src/EntityPsuedoPropertyFlags.h b/libraries/entities/src/EntityPsuedoPropertyFlags.h index 0b051a4c74..26c2a14015 100644 --- a/libraries/entities/src/EntityPsuedoPropertyFlags.h +++ b/libraries/entities/src/EntityPsuedoPropertyFlags.h @@ -32,6 +32,8 @@ namespace EntityPsuedoPropertyFlag { RenderInfo, ClientOnly, OwningAvatarID, + AvatarEntity, + LocalEntity, NumFlags }; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3491688588..969e69f048 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -470,7 +470,7 @@ void synchronizeEditedGrabProperties(EntityItemProperties& properties, const QSt } -QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { +QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, const QString& entityHostString) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.addedEntityCount++; @@ -479,8 +479,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties const auto sessionID = nodeList->getSessionUUID(); EntityItemProperties propertiesWithSimID = properties; - if (clientOnly) { - propertiesWithSimID.setClientOnly(clientOnly); + propertiesWithSimID.setEntityHostFromString(entityHostString); + if (propertiesWithSimID.getEntityHost() == EntityHost::AVATAR_ENTITY) { propertiesWithSimID.setOwningAvatarID(sessionID); } @@ -568,8 +568,11 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { bool cloneAvatarEntity = properties.getCloneAvatarEntity(); properties.convertToCloneProperties(entityIDToClone); - if (cloneAvatarEntity) { - return addEntity(properties, true); + if (properties.getEntityHost() == EntityHost::LOCAL_ENTITY) { + // Local entities are only cloned locally + return addEntity(properties, "local"); + } else if (cloneAvatarEntity) { + return addEntity(properties, "avatar"); } else { // 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 @@ -681,6 +684,10 @@ QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScri psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::ClientOnly); } else if (extendedPropertyString == "owningAvatarID") { psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::OwningAvatarID); + } else if (extendedPropertyString == "avatarEntity") { + psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::AvatarEntity); + } else if (extendedPropertyString == "localEntity") { + psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::LocalEntity); } }; @@ -784,7 +791,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& return; } - if (entity->getClientOnly() && entity->getOwningAvatarID() != sessionID) { + if (entity->isAvatarEntity() && entity->getOwningAvatarID() != sessionID) { // don't edit other avatar's avatarEntities properties = EntityItemProperties(); return; @@ -827,7 +834,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } // set these to make EntityItemProperties::getScalesWithParent() work correctly - properties.setClientOnly(entity->getClientOnly()); + properties.setEntityHost(entity->getEntityHost()); properties.setOwningAvatarID(entity->getOwningAvatarID()); // make sure the properties has a type, so that the encode can know which properties to include @@ -952,7 +959,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); - if (entity->getClientOnly() && entity->getOwningAvatarID() != myNodeID) { + if (entity->isAvatarEntity() && entity->getOwningAvatarID() != myNodeID) { // don't delete other avatar's avatarEntities // If you actually own the entity but the onwership property is not set because of a domain switch // The lines below makes sure the entity is deleted once its properties are set. @@ -967,11 +974,11 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { shouldSendDeleteToServer = false; } else { // only delete local entities, server entities will round trip through the server filters - if (entity->getClientOnly() || _entityTree->isServerlessMode()) { + if (entity->isAvatarEntity() || _entityTree->isServerlessMode()) { shouldSendDeleteToServer = false; _entityTree->deleteEntity(entityID); - if (entity->getClientOnly() && getEntityPacketSender()->getMyAvatar()) { + if (entity->isAvatarEntity() && getEntityPacketSender()->getMyAvatar()) { getEntityPacketSender()->getMyAvatar()->clearAvatarEntity(entityID, false); } } @@ -1638,14 +1645,14 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return; } - if (entity->getClientOnly() && entity->getOwningAvatarID() != myNodeID) { + if (entity->isAvatarEntity() && entity->getOwningAvatarID() != myNodeID) { return; } doTransmit = actor(simulation, entity); _entityTree->entityChanged(entity); if (doTransmit) { - properties.setClientOnly(entity->getClientOnly()); + properties.setEntityHost(entity->getEntityHost()); properties.setOwningAvatarID(entity->getOwningAvatarID()); } }); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index df7b0df9a1..78d71ac76b 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -233,13 +233,29 @@ public slots: */ Q_INVOKABLE bool canReplaceContent(); + /**jsdoc + *

How an entity is sent over the wire.

+ * + * + * + * + * + * + * + * + * + *
ValueDescription
domainDomain entities are sent over the entity server to everyone else
avatarAvatar entities are sent over the avatar entity and are associated with one avatar
localLocal entities are not sent over the wire and will only render for you, locally
+ * @typedef {string} EntityHost + */ + /**jsdoc * Add a new entity with specified properties. * @function Entities.addEntity * @param {Entities.EntityProperties} properties - The properties of the entity to create. - * @param {boolean} [clientOnly=false] - If true, or if clientOnly is set true in - * the properties, the entity is created as an avatar entity; otherwise it is created on the server. An avatar entity + * @param {EntityHost} [entityHost="domain"] - If "avatar" the entity is created as an avatar entity. An avatar entity * follows you to each domain you visit, rendering at the same world coordinates unless it's parented to your avatar. + * If "local", the entity is created as a local entity, which will only render for you and isn't sent over the wire. + * Otherwise it is created as a normal entity and sent over the entity server. * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. * @example Create a box entity in front of your avatar. * var entityID = Entities.addEntity({ @@ -250,7 +266,19 @@ public slots: * }); * print("Entity created: " + entityID); */ - Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); + Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, const QString& entityHostString); + + /**jsdoc + * Add a new entity with specified properties. + * @function Entities.addEntity + * @param {Entities.EntityProperties} properties - The properties of the entity to create. + * @param {boolean} [avatarEntity=false] - Whether to create an avatar entity or a domain entity + * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. + */ + Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool avatarEntity = false) { + QString entityHost = avatarEntity ? "avatar" : "domain"; + return addEntity(properties, entityHost); + } /// temporary method until addEntity can be used from QJSEngine /// Deliberately not adding jsdoc, only used internally. @@ -896,8 +924,7 @@ public slots: Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); /**jsdoc - * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about — domain - * and client-only — to the program log. + * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about to the program log. * @function Entities.dumpTree */ Q_INVOKABLE void dumpTree() const; @@ -1870,7 +1897,7 @@ signals: /**jsdoc * Triggered when an entity is added to Interface's local in-memory tree of entities it knows about. This may occur when * entities are loaded upon visiting a domain, when the user rotates their view so that more entities become visible, and - * when a domain or client-only entity is added (e.g., by {@Entities.addEntity|addEntity}). + * when any type of entity is added (e.g., by {@Entities.addEntity|addEntity}). * @function Entities.addingEntity * @param {Uuid} entityID - The ID of the entity added. * @returns {Signal} diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 16d7e74703..1abdd15314 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -541,7 +541,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti return nullptr; } - if (!properties.getClientOnly() && getIsClient() && + if (properties.getEntityHost() == EntityHost::DOMAIN_ENTITY && getIsClient() && !nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() && !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) { return nullptr; @@ -2668,7 +2668,15 @@ bool EntityTree::readFromMap(QVariantMap& map) { entityItemID = EntityItemID(QUuid::createUuid()); } - if (properties.getClientOnly()) { + // Convert old clientOnly bool to new entityHost enum + // (must happen before setOwningAvatarID below) + if (contentVersion < (int)EntityVersion::EntityHosts) { + if (entityMap.contains("clientOnly")) { + properties.setEntityHost(entityMap["clientOnly"].toBool() ? EntityHost::AVATAR_ENTITY : EntityHost::DOMAIN_ENTITY); + } + } + + if (properties.getEntityHost() == EntityHost::AVATAR_ENTITY) { auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); properties.setOwningAvatarID(myNodeID); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 9eed463d2d..62ff742793 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::FixedLightSerialization); + return static_cast(EntityVersion::EntityHosts); 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 37a4b32940..284760ea38 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -245,7 +245,8 @@ enum class EntityVersion : PacketVersion { BloomEffect, GrabProperties, ScriptGlmVectors, - FixedLightSerialization + FixedLightSerialization, + EntityHosts }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c920665279..aaae31abb1 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -80,8 +80,8 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer // rather than pass the legit shape pointer to the ObjectMotionState ctor above. setShape(shape); - if (_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) { - // client-only entities are always thus, so we cache this fact in _ownershipState + if (_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) { + // avatar entities entities are always thus, so we cache this fact in _ownershipState _ownershipState = EntityMotionState::OwnershipState::Unownable; } @@ -430,7 +430,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { assert(entityTreeIsLocked()); // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor - assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); + assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); if (_entity->getTransitingWithAvatar()) { return false; @@ -595,7 +595,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityTreeElementPointer element = _entity->getElement(); EntityTreePointer tree = element ? element->getTree() : nullptr; - properties.setClientOnly(_entity->getClientOnly()); + properties.setEntityHost(_entity->getEntityHost()); properties.setOwningAvatarID(_entity->getOwningAvatarID()); entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties); @@ -610,7 +610,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); newQueryCubeProperties.setLastEdited(properties.getLastEdited()); - newQueryCubeProperties.setClientOnly(entityDescendant->getClientOnly()); + newQueryCubeProperties.setEntityHost(entityDescendant->getEntityHost()); newQueryCubeProperties.setOwningAvatarID(entityDescendant->getOwningAvatarID()); entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index ab8bfb8290..8e5248f6a9 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -79,7 +79,7 @@ void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) { _deadEntities.insert(entity); } } - if (entity->getClientOnly()) { + if (entity->isAvatarEntity()) { _deadAvatarEntities.insert(entity); } } @@ -339,7 +339,7 @@ void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotio EntityItemPointer entity = entityState->getEntity(); _entitiesToSort.insert(entity); if (!serverlessMode) { - if (entity->getClientOnly()) { + if (entity->isAvatarEntity()) { switch (entityState->getOwnershipState()) { case EntityMotionState::OwnershipState::PendingBid: _bids.removeFirst(entityState); diff --git a/scripts/system/attachedEntitiesManager.js b/scripts/system/attachedEntitiesManager.js index c0144e772f..061e27f595 100644 --- a/scripts/system/attachedEntitiesManager.js +++ b/scripts/system/attachedEntitiesManager.js @@ -186,7 +186,7 @@ function AttachedEntitiesManager() { delete wearProps.actionData; delete wearProps.sittingPoints; delete wearProps.boundingBox; - delete wearProps.clientOnly; + delete wearProps.avatarEntity; delete wearProps.owningAvatarID; delete wearProps.localPosition; delete wearProps.localRotation; diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index a55ed74a86..16a5760358 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -328,8 +328,8 @@ function isGrabbable(entityID) { return false; } - var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'grab.grabbable']); - if (properties.clientOnly) { + var properties = Entities.getEntityProperties(entityID, ['avatarEntity', 'grab.grabbable']); + if (properties.avatarEntity) { return properties.grab.grabbable; } @@ -337,8 +337,8 @@ function isGrabbable(entityID) { } function setGrabbable(entityID, grabbable) { - var properties = Entities.getEntityProperties(entityID, ['clientOnly']); - if (properties.clientOnly) { + var properties = Entities.getEntityProperties(entityID, ['avatarEntity']); + if (properties.avatarEntity) { Entities.editEntity(entityID, { grab: { grabbable: grabbable }}); } } diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 8e5b41deda..f29edd8ff9 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -97,9 +97,8 @@ cleanUpOldMaterialEntities = function() { * @param width [number] width in meters of the tablet model * @param dpi [number] dpi of web surface used to show the content. * @param hand [number] -1 indicates no hand, Controller.Standard.RightHand or Controller.Standard.LeftHand - * @param clientOnly [bool] true indicates tablet model is only visible to client. */ -WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { +WebTablet = function (url, width, dpi, hand, location, visible) { var _this = this; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 168c81db1f..d1dbd31019 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -264,7 +264,7 @@ SelectionManager = (function() { if (properties === undefined) { properties = Entities.getEntityProperties(originalEntityID); } - if (!properties.locked && (!properties.clientOnly || properties.owningAvatarID === MyAvatar.sessionUUID)) { + if (!properties.locked && (!properties.avatarEntity || properties.owningAvatarID === MyAvatar.sessionUUID)) { if (nonDynamicEntityIsBeingGrabbedByAvatar(properties)) { properties.parentID = null; properties.parentJointIndex = null; diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js index d90695c767..d3e5c96739 100644 --- a/scripts/system/marketplaces/marketplace.js +++ b/scripts/system/marketplaces/marketplace.js @@ -30,7 +30,7 @@ var TOOLBAR_MARGIN_Y = 0; var marketplaceVisible = false; var marketplaceWebTablet; -// We persist clientOnly data in the .ini file, and reconsistitute it on restart. +// We persist avatarEntity data in the .ini file, and reconsistitute it on restart. // To keep things consistent, we pickle the tablet data in Settings, and kill any existing such on restart and domain change. var persistenceKey = "io.highfidelity.lastDomainTablet"; diff --git a/scripts/system/nameTag.js b/scripts/system/nameTag.js index 69e5dac1bf..147da25779 100644 --- a/scripts/system/nameTag.js +++ b/scripts/system/nameTag.js @@ -12,7 +12,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -const CLIENTONLY = false; +const AVATARENTITY = false; const ENTITY_CHECK_INTERVAL = 5000; // ms = 5 seconds const STARTUP_DELAY = 2000; // ms = 2 second const OLD_AGE = 3500; // we recreate the entity if older than this time in seconds @@ -43,7 +43,7 @@ function addNameTag() { dimensions: dimensionsFromName(), position: nameTagPosition } - nameTagEntityID = Entities.addEntity(nameTagProperties, CLIENTONLY); + nameTagEntityID = Entities.addEntity(nameTagProperties, AVATARENTITY); } function updateNameTag() { diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 9a22014895..f9e9165f2e 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -108,7 +108,7 @@ tabletScalePercentage = getTabletScalePercentageFromSettings(); UIWebTablet = new WebTablet("hifi/tablet/TabletRoot.qml", DEFAULT_WIDTH * (tabletScalePercentage / 100), - null, activeHand, true, null, false); + null, activeHand, null, false); UIWebTablet.register(); HMD.tabletID = UIWebTablet.tabletEntityID; HMD.homeButtonID = UIWebTablet.homeButtonID; From 8dbb2d806be046a38500ca1de49a015b10045aff Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 14 Nov 2018 14:48:07 -0800 Subject: [PATCH 02/22] local entities are always collisionless --- libraries/entities/src/EntityItemProperties.cpp | 8 +++++++- libraries/entities/src/EntityScriptingInterface.cpp | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index ff8da64f60..bcd53a2420 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3977,7 +3977,13 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setParentJointIndex(-1); setLifetime(getCloneLifetime()); setDynamic(getCloneDynamic()); - setEntityHost(getCloneAvatarEntity() ? EntityHost::AVATAR_ENTITY : EntityHost::DOMAIN_ENTITY); + if (getEntityHost() != EntityHost::LOCAL_ENTITY) { + setEntityHost(getCloneAvatarEntity() ? EntityHost::AVATAR_ENTITY : EntityHost::DOMAIN_ENTITY); + } else { + // Local Entities clone as local entities + setEntityHost(EntityHost::LOCAL_ENTITY); + setCollisionless(true); + } setCreated(usecTimestampNow()); setLastEdited(usecTimestampNow()); setCloneable(ENTITY_ITEM_DEFAULT_CLONEABLE); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 969e69f048..ffd7748c37 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -482,6 +482,10 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties propertiesWithSimID.setEntityHostFromString(entityHostString); if (propertiesWithSimID.getEntityHost() == EntityHost::AVATAR_ENTITY) { propertiesWithSimID.setOwningAvatarID(sessionID); + } else if (propertiesWithSimID.getEntityHost() == EntityHost::LOCAL_ENTITY) { + // For now, local entities are always collisionless + // TODO: create a separate, local physics simulation that just handles local entities (and MyAvatar?) + propertiesWithSimID.setCollisionless(true); } propertiesWithSimID.setLastEditedBy(sessionID); @@ -834,7 +838,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } // set these to make EntityItemProperties::getScalesWithParent() work correctly - properties.setEntityHost(entity->getEntityHost()); + EntityHost entityHost = entity->getEntityHost(); + properties.setEntityHost(entityHost); + if (entityHost == EntityHost::LOCAL_ENTITY) { + properties.setCollisionless(true); + } properties.setOwningAvatarID(entity->getOwningAvatarID()); // make sure the properties has a type, so that the encode can know which properties to include From 17404863e7e3971f7b4e21d62c939f3f2af62c9b Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 15 Nov 2018 17:42:51 -0800 Subject: [PATCH 03/22] entity list reordering WIP --- scripts/system/html/css/edit-style.css | 4 ++ scripts/system/html/js/entityList.js | 97 ++++++++++++++++++++------ 2 files changed, 81 insertions(+), 20 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 3a291bf27b..a00dc4d57c 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1383,6 +1383,10 @@ input[type=button]#export { cursor: col-resize; } +#entity-table .dragging { + background-color: #b3ecff; +} + #entity-table td { box-sizing: border-box; } diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 00c50169a6..6f12dd1e7c 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -21,6 +21,8 @@ const MAX_LENGTH_RADIUS = 9; const MINIMUM_COLUMN_WIDTH = 24; const SCROLLBAR_WIDTH = 20; const RESIZER_WIDTH = 10; +const DELTA_X_MOVE_COLUMNS_THRESHOLD = 2; +const DELTA_X_COLUMN_SWAP_POSITION = 5; const COLUMNS = { type: { @@ -177,10 +179,12 @@ let isFilterInView = false; let columns = []; let columnsByID = {}; -let currentResizeEl = null; -let startResizeEvent = null; +let lastResizeEvent = null; let resizeColumnIndex = 0; -let startThClick = null; +let elTargetTh = null; +let targetColumnIndex = 0; +let lastColumnSwapPosition = -1; +let initialThEvent = null; let elEntityTable, elEntityTableHeader, @@ -319,7 +323,7 @@ function loaded() { let thID = "entity-" + columnID; let elTh = document.createElement("th"); elTh.setAttribute("id", thID); - elTh.setAttribute("data-resizable-column-id", thID); + elTh.setAttribute("columnIndex", columnIndex); if (columnData.glyph) { let elGlyph = document.createElement("span"); elGlyph.className = "glyph"; @@ -328,14 +332,10 @@ function loaded() { } else { elTh.innerText = columnData.columnHeader; } - elTh.onmousedown = function() { - startThClick = this; - }; - elTh.onmouseup = function() { - if (startThClick === this) { - setSortColumn(columnID); - } - startThClick = null; + elTh.onmousedown = function(event) { + elTargetTh = event.target; + initialThEvent = event; + targetColumnIndex = parseInt(elTargetTh.getAttribute("columnIndex")); }; let elResizer = document.createElement("span"); @@ -1030,7 +1030,7 @@ function loaded() { } function onStartResize(event) { - startResizeEvent = event; + lastResizeEvent = event; resizeColumnIndex = parseInt(this.getAttribute("columnIndex")); event.stopPropagation(); } @@ -1074,8 +1074,33 @@ function loaded() { entityList.refresh(); } - document.onmousemove = function(ev) { - if (startResizeEvent) { + function swapColumns(columnAIndex, columnBIndex) { + let columnA = columns[columnAIndex]; + let columnB = columns[columnBIndex]; + let columnATh = columns[columnAIndex].elTh; + let columnBTh = columns[columnBIndex].elTh; + let columnThParent = columnATh.parentNode; + columnThParent.removeChild(columnBTh); + columnThParent.insertBefore(columnBTh, columnATh); + columnATh.setAttribute("columnIndex", columnBIndex); + columnBTh.setAttribute("columnIndex", columnAIndex); + columnA.elResizer.setAttribute("columnIndex", columnBIndex); + columnB.elResizer.setAttribute("columnIndex", columnAIndex); + + for (let i = 0; i < visibleEntities.length; ++i) { + let elRow = visibleEntities[i].elRow; + let columnACell = elRow.childNodes[columnAIndex]; + let columnBCell = elRow.childNodes[columnBIndex]; + elRow.removeChild(columnBCell); + elRow.insertBefore(columnBCell, columnACell); + } + + columns[columnAIndex] = columnB; + columns[columnBIndex] = columnA; + } + + document.onmousemove = function(event) { + if (lastResizeEvent) { startTh = null; let column = columns[resizeColumnIndex]; @@ -1087,7 +1112,7 @@ function loaded() { } let fullWidth = elEntityTableBody.offsetWidth; - let dx = ev.clientX - startResizeEvent.clientX; + let dx = event.clientX - lastResizeEvent.clientX; let dPct = dx / fullWidth; let newColWidth = column.width + dPct; @@ -1097,14 +1122,46 @@ function loaded() { column.width += dPct; nextColumn.width -= dPct; updateColumnWidths(); - startResizeEvent = ev; + lastResizeEvent = event; + } + } else if (elTargetTh) { + let dxFromInitial = event.clientX - initialThEvent.clientX; + if (Math.abs(dxFromInitial) >= DELTA_X_MOVE_COLUMNS_THRESHOLD) { + elTargetTh.className = "dragging"; + } + if (targetColumnIndex < columns.length - 1) { + let nextColumnIndex = targetColumnIndex + 1; + let nextColumnTh = columns[nextColumnIndex].elTh; + let nextColumnStartX = nextColumnTh.getBoundingClientRect().left; + if (event.clientX >= nextColumnStartX && event.clientX - lastColumnSwapPosition >= DELTA_X_COLUMN_SWAP_POSITION) { + swapColumns(targetColumnIndex, nextColumnIndex); + targetColumnIndex = nextColumnIndex; + lastColumnSwapPosition = event.clientX; + } + } + if (targetColumnIndex >= 1) { + let prevColumnIndex = targetColumnIndex - 1; + let prevColumnTh = columns[prevColumnIndex].elTh; + let prevColumnEndX = prevColumnTh.getBoundingClientRect().right; + if (event.clientX <= prevColumnEndX && event.clientX - lastColumnSwapPosition >= DELTA_X_COLUMN_SWAP_POSITION) { + swapColumns(prevColumnIndex, targetColumnIndex); + targetColumnIndex = prevColumnIndex; + } } } } - document.onmouseup = function(ev) { - startResizeEvent = null; - ev.stopPropagation(); + document.onmouseup = function(event) { + if (elTargetTh) { + if (elTargetTh.className !== "dragging" && elTargetTh === event.target) { + let columnID = columns[targetColumnIndex].columnID; + setSortColumn(columnID); + } + elTargetTh.className = ""; + } + lastResizeEvent = null; + elTargetTh = null; + initialThEvent = null; } function setSpaceMode(spaceMode) { From a0f79f988194a74f19e82e104241572da7ff7d30 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Nov 2018 17:14:30 -0800 Subject: [PATCH 04/22] entity list reordering columns --- scripts/system/html/js/entityList.js | 68 ++++++++++++++++++---------- scripts/system/html/js/listView.js | 4 -- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index e1581b7877..963611f059 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -110,8 +110,8 @@ const COLUMNS = { }; const COMPARE_ASCENDING = function(a, b) { - let va = a[currentSortColumn]; - let vb = b[currentSortColumn]; + let va = a[currentSortColumnID]; + let vb = b[currentSortColumnID]; if (va < vb) { return -1; @@ -171,7 +171,7 @@ let entityList = null; // The ListView */ let entityListContextMenu = null; -let currentSortColumn = 'type'; +let currentSortColumnID = 'type'; let currentSortOrder = ASCENDING_SORT; let elSortOrders = {}; let typeFilters = []; @@ -182,6 +182,7 @@ let columnsByID = {}; let lastResizeEvent = null; let resizeColumnIndex = 0; let elTargetTh = null; +let elTargetSpan = null; let targetColumnIndex = 0; let lastColumnSwapPosition = -1; let initialThEvent = null; @@ -226,10 +227,6 @@ const PROFILE = !ENABLE_PROFILING ? PROFILE_NOOP : function(name, fn, args) { console.log("PROFILE-Web " + profileIndent + "(" + name + ") End " + delta + "ms"); }; -debugPrint = function (message) { - console.log(message); -}; - function loaded() { openEventBridge(function() { elEntityTable = document.getElementById("entity-table"); @@ -320,10 +317,11 @@ function loaded() { for (let columnID in COLUMNS) { let columnData = COLUMNS[columnID]; - let thID = "entity-" + columnID; let elTh = document.createElement("th"); + let thID = "entity-" + columnID; elTh.setAttribute("id", thID); elTh.setAttribute("columnIndex", columnIndex); + elTh.setAttribute("columnID", columnID); if (columnData.glyph) { let elGlyph = document.createElement("span"); elGlyph.className = "glyph"; @@ -333,15 +331,19 @@ function loaded() { elTh.innerText = columnData.columnHeader; } elTh.onmousedown = function(event) { - elTargetTh = event.target; + if (event.target.nodeName === 'TH') { + elTargetTh = event.target; + targetColumnIndex = parseInt(elTargetTh.getAttribute("columnIndex")); + lastColumnSwapPosition = event.clientX; + } else if (event.target.nodeName === 'SPAN') { + elTargetSpan = event.target; + } initialThEvent = event; - targetColumnIndex = parseInt(elTargetTh.getAttribute("columnIndex")); }; let elResizer = document.createElement("span"); elResizer.className = "resizer"; elResizer.innerHTML = " "; - elResizer.setAttribute("columnIndex", columnIndex); elResizer.onmousedown = onStartResize; elTh.appendChild(elResizer); @@ -699,13 +701,13 @@ function loaded() { refreshNoEntitiesMessage(); } - function setSortColumn(column) { + function setSortColumn(columnID) { PROFILE("set-sort-column", function() { - if (currentSortColumn === column) { + if (currentSortColumnID === columnID) { currentSortOrder *= -1; } else { - elSortOrders[currentSortColumn].innerHTML = ""; - currentSortColumn = column; + elSortOrders[currentSortColumnID].innerHTML = ""; + currentSortColumnID = columnID; currentSortOrder = ASCENDING_SORT; } refreshSortOrder(); @@ -714,7 +716,7 @@ function loaded() { } function refreshSortOrder() { - elSortOrders[currentSortColumn].innerHTML = currentSortOrder === ASCENDING_SORT ? ASCENDING_STRING : DESCENDING_STRING; + elSortOrders[currentSortColumnID].innerHTML = currentSortOrder === ASCENDING_SORT ? ASCENDING_STRING : DESCENDING_STRING; } function refreshEntities() { @@ -1031,7 +1033,7 @@ function loaded() { function onStartResize(event) { lastResizeEvent = event; - resizeColumnIndex = parseInt(this.getAttribute("columnIndex")); + resizeColumnIndex = parseInt(this.parentNode.getAttribute("columnIndex")); event.stopPropagation(); } @@ -1089,14 +1091,18 @@ function loaded() { for (let i = 0; i < visibleEntities.length; ++i) { let elRow = visibleEntities[i].elRow; - let columnACell = elRow.childNodes[columnAIndex]; - let columnBCell = elRow.childNodes[columnBIndex]; - elRow.removeChild(columnBCell); - elRow.insertBefore(columnBCell, columnACell); + if (elRow) { + let columnACell = elRow.childNodes[columnAIndex]; + let columnBCell = elRow.childNodes[columnBIndex]; + elRow.removeChild(columnBCell); + elRow.insertBefore(columnBCell, columnACell); + } } columns[columnAIndex] = columnB; columns[columnBIndex] = columnA; + + updateColumnWidths(); } document.onmousemove = function(event) { @@ -1143,24 +1149,38 @@ function loaded() { let prevColumnIndex = targetColumnIndex - 1; let prevColumnTh = columns[prevColumnIndex].elTh; let prevColumnEndX = prevColumnTh.getBoundingClientRect().right; - if (event.clientX <= prevColumnEndX && event.clientX - lastColumnSwapPosition >= DELTA_X_COLUMN_SWAP_POSITION) { + if (event.clientX <= prevColumnEndX && lastColumnSwapPosition - event.clientX >= DELTA_X_COLUMN_SWAP_POSITION) { swapColumns(prevColumnIndex, targetColumnIndex); - targetColumnIndex = prevColumnIndex; + targetColumnIndex = prevColumnIndex; + lastColumnSwapPosition = event.clientX; } } + } else if (elTargetSpan) { + let dxFromInitial = event.clientX - initialThEvent.clientX; + if (Math.abs(dxFromInitial) >= DELTA_X_MOVE_COLUMNS_THRESHOLD) { + elTargetTh = elTargetSpan.parentNode; + elTargetTh.className = "dragging"; + targetColumnIndex = parseInt(elTargetTh.getAttribute("columnIndex")); + lastColumnSwapPosition = event.clientX; + elTargetSpan = null; + } } } document.onmouseup = function(event) { if (elTargetTh) { if (elTargetTh.className !== "dragging" && elTargetTh === event.target) { - let columnID = columns[targetColumnIndex].columnID; + let columnID = elTargetTh.getAttribute("columnID"); setSortColumn(columnID); } elTargetTh.className = ""; + } else if (elTargetSpan) { + let columnID = elTargetSpan.parentNode.getAttribute("columnID"); + setSortColumn(columnID); } lastResizeEvent = null; elTargetTh = null; + elTargetSpan = null; initialThEvent = null; } diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index f775a4cb24..77e1ba99b1 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -9,10 +9,6 @@ const SCROLL_ROWS = 2; // number of rows used as scrolling buffer, each time we pass this number of rows we scroll const FIRST_ROW_INDEX = 2; // the first elRow element's index in the child nodes of the table body -debugPrint = function (message) { - console.log(message); -}; - function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunction, updateRowFunction, clearRowFunction, WINDOW_NONVARIABLE_HEIGHT) { this.elTableBody = elTableBody; From a94fec75b47cf1372797521424c7057fe5322373 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 19 Nov 2018 17:59:42 -0800 Subject: [PATCH 05/22] prevent drag drop onto edit --- scripts/system/html/entityList.html | 1 + scripts/system/html/entityProperties.html | 1 + scripts/system/html/gridControls.html | 1 + scripts/system/html/js/entityList.js | 3 ++- scripts/system/html/js/entityProperties.js | 1 + scripts/system/html/js/gridControls.js | 1 + scripts/system/html/js/utils.js | 23 ++++++++++++++++++++++ 7 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 scripts/system/html/js/utils.js diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 6915a45f19..f9948bc8b0 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -18,6 +18,7 @@ + diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 01395368b0..a8bcabbfea 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -23,6 +23,7 @@ + diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index 4be002619a..8d6ee34bc0 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -16,6 +16,7 @@ + diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 84ad59df36..eb3e9ed882 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -1286,8 +1286,9 @@ function loaded() { }); augmentSpinButtons(); + disableDragDrop(); - document.addEventListener("contextmenu", function (event) { + document.addEventListener("contextmenu", function(event) { entityListContextMenu.close(); // Disable default right-click context menu which is not visible in the HMD and makes it seem like the app has locked diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index d3e0751732..6dc563b102 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3479,6 +3479,7 @@ function loaded() { }); augmentSpinButtons(); + disableDragDrop(); // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked document.addEventListener("contextmenu", function(event) { diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index b2d5988938..c2183130e9 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -108,6 +108,7 @@ function loaded() { }); augmentSpinButtons(); + disableDragDrop(); EventBridge.emitWebEvent(JSON.stringify({ type: 'init' })); }); diff --git a/scripts/system/html/js/utils.js b/scripts/system/html/js/utils.js new file mode 100644 index 0000000000..fe96e8b79e --- /dev/null +++ b/scripts/system/html/js/utils.js @@ -0,0 +1,23 @@ +// +// utils.js +// +// Created by David Back on 19 Nov 2018 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function disableDragDrop() { + document.addEventListener("drop", function(event) { + event.preventDefault(); + event.dataTransfer.effectAllowed = "none"; + event.dataTransfer.dropEffect = "none"; + }, false); + + document.addEventListener("dragover", function(event) { + event.preventDefault(); + event.dataTransfer.effectAllowed = "none"; + event.dataTransfer.dropEffect = "none"; + }, false); +} From b2bd0d99de4b05f41c5f66b1f819c713437a0df0 Mon Sep 17 00:00:00 2001 From: birarda Date: Tue, 20 Nov 2018 16:07:48 -0800 Subject: [PATCH 06/22] add a warning for cloud domain ID changes --- .../resources/web/settings/js/settings.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 3888277c00..2950b8de75 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -18,9 +18,6 @@ $(document).ready(function(){ Settings.extraGroupsAtIndex = Settings.extraDomainGroupsAtIndex; Settings.afterReloadActions = function() { - // append the domain selection modal - appendDomainIDButtons(); - // call our method to setup the HF account button setupHFAccountButton(); @@ -52,6 +49,11 @@ $(document).ready(function(){ if (cloudWizardExit != undefined) { $('#cloud-domains-alert').show(); } + + $(Settings.DOMAIN_ID_SELECTOR).siblings('span').append("
Changing the domain ID for a Cloud Domain may result in an incorrect status for the domain on your Cloud Domains page."); + } else { + // append the domain selection modal + appendDomainIDButtons(); } handleAction(); @@ -59,9 +61,9 @@ $(document).ready(function(){ Settings.handlePostSettings = function(formJSON) { - if (!verifyAvatarHeights()) { - return false; - } + if (!verifyAvatarHeights()) { + return false; + } // check if we've set the basic http password if (formJSON["security"]) { From e282d51883aa891350de859f64553fdcfdc961bb Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 26 Nov 2018 01:15:07 -0500 Subject: [PATCH 07/22] CR --- interface/src/AvatarBookmarks.cpp | 2 +- .../scripting/WalletScriptingInterface.cpp | 2 +- .../ui/overlays/ContextOverlayInterface.cpp | 4 +- .../src/avatars-renderer/Avatar.cpp | 2 +- .../src/RenderableEntityItem.cpp | 10 +-- .../entities/src/EntityEditPacketSender.cpp | 4 +- libraries/entities/src/EntityItem.cpp | 10 +-- libraries/entities/src/EntityItem.h | 26 +++++--- .../entities/src/EntityItemProperties.cpp | 64 +++++++++---------- libraries/entities/src/EntityItemProperties.h | 4 +- .../entities/src/EntityPropertyFlags.cpp | 2 +- libraries/entities/src/EntityPropertyFlags.h | 2 +- .../entities/src/EntityScriptingInterface.cpp | 18 +++--- .../entities/src/EntityScriptingInterface.h | 10 +-- libraries/entities/src/EntityTree.cpp | 10 +-- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 4 +- 18 files changed, 92 insertions(+), 86 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index df7620013c..ae14fb5064 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -58,7 +58,7 @@ void addAvatarEntities(const QVariantList& avatarEntities) { EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties); entityProperties.setParentID(myNodeID); - entityProperties.setEntityHost(EntityHost::AVATAR_ENTITY); + entityProperties.setEntityHostType(entity::HostType::AVATAR); entityProperties.setOwningAvatarID(myNodeID); entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY); entityProperties.markAllChanged(); diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 6ad81f0e2b..60f850adac 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -35,7 +35,7 @@ void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUui QSharedPointer contextOverlayInterface = DependencyManager::get(); EntityItemProperties entityProperties = DependencyManager::get()->getEntityProperties(entityID, contextOverlayInterface->getEntityPropertyFlags()); - if (entityProperties.getEntityHost() == EntityHost::AVATAR_ENTITY) { + if (entityProperties.getEntityHostType() == entity::HostType::AVATAR) { if (!entityID.isNull() && entityProperties.getCertificateID().length() > 0) { contextOverlayInterface->requestOwnershipVerification(entityID); } else { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index f6c213163d..d077a2fe51 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -46,7 +46,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; _entityPropertyFlags += PROP_CERTIFICATE_ID; - _entityPropertyFlags += PROP_ENTITY_HOST; + _entityPropertyFlags += PROP_ENTITY_HOST_TYPE; _entityPropertyFlags += PROP_OWNING_AVATAR_ID; auto entityScriptingInterface = DependencyManager::get().data(); @@ -296,7 +296,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID auto nodeList = DependencyManager::get(); if (entityProperties.verifyStaticCertificateProperties()) { - if (entityProperties.getEntityHost() == EntityHost::AVATAR_ENTITY) { + if (entityProperties.getEntityHostType() == entity::HostType::AVATAR) { SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); if (entityServer) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index fcac61a0bd..6398426690 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -389,7 +389,7 @@ void Avatar::updateAvatarEntities() { QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); EntityItemProperties properties; EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, properties); - properties.setEntityHost(EntityHost::AVATAR_ENTITY); + properties.setEntityHostType(entity::HostType::AVATAR); properties.setOwningAvatarID(getID()); // there's no entity-server to tell us we're the simulation owner, so always set the diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 9b9d0bb7a9..75d06191ea 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -39,7 +39,7 @@ enum class RenderItemStatusIcon { SIMULATION_OWNER = 3, HAS_ACTIONS = 4, OTHER_SIMULATION_OWNER = 5, - ENTITY_HOST = 6, + ENTITY_HOST_TYPE = 6, NONE = 255 }; @@ -118,17 +118,17 @@ void EntityRenderer::makeStatusGetters(const EntityItemPointer& entity, Item::St if (entity->isAvatarEntity()) { if (entity->getOwningAvatarID() == myNodeID) { return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN, - (unsigned char)RenderItemStatusIcon::ENTITY_HOST); + (unsigned char)RenderItemStatusIcon::ENTITY_HOST_TYPE); } else { return render::Item::Status::Value(1.0f, render::Item::Status::Value::RED, - (unsigned char)RenderItemStatusIcon::ENTITY_HOST); + (unsigned char)RenderItemStatusIcon::ENTITY_HOST_TYPE); } } else if (entity->isLocalEntity()) { return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE, - (unsigned char)RenderItemStatusIcon::ENTITY_HOST); + (unsigned char)RenderItemStatusIcon::ENTITY_HOST_TYPE); } return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN, - (unsigned char)RenderItemStatusIcon::ENTITY_HOST); + (unsigned char)RenderItemStatusIcon::ENTITY_HOST_TYPE); }); } diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 0169bb6d9c..c414a7a4ac 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -84,7 +84,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityTreePointer entityTree, EntityItemID entityItemID, const EntityItemProperties& properties) { - if (properties.getEntityHost() == EntityHost::AVATAR_ENTITY) { + if (properties.getEntityHostType() == entity::HostType::AVATAR) { if (!_myAvatar) { qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit with no myAvatar"; } else if (properties.getOwningAvatarID() == _myAvatar->getID()) { @@ -94,7 +94,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit for another avatar"; } return; - } else if (properties.getEntityHost() == EntityHost::LOCAL_ENTITY) { + } else if (properties.getEntityHostType() == entity::HostType::LOCAL) { // Don't send edits for local entities return; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8ffde04b23..5855306994 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -119,7 +119,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_PARENT_JOINT_INDEX; requestedProperties += PROP_QUERY_AA_CUBE; - requestedProperties += PROP_ENTITY_HOST; + requestedProperties += PROP_ENTITY_HOST_TYPE; requestedProperties += PROP_OWNING_AVATAR_ID; requestedProperties += PROP_LAST_EDITED_BY; @@ -172,7 +172,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); - requestedProperties -= PROP_ENTITY_HOST; + requestedProperties -= PROP_ENTITY_HOST_TYPE; requestedProperties -= PROP_OWNING_AVATAR_ID; // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, @@ -1334,7 +1334,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(localPosition, getLocalPosition); COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRotation, getLocalOrientation); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityHost, getEntityHost); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityHostType, getEntityHostType); COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy); @@ -1476,7 +1476,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityHost, setEntityHost); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityHostType, setEntityHostType); SET_ENTITY_PROPERTY_FROM_PROPERTIES(owningAvatarID, setOwningAvatarID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); @@ -3274,7 +3274,7 @@ void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properti properties.setSimulationOwner(Physics::getSessionUUID(), priority); setPendingOwnershipPriority(priority); - properties.setEntityHost(getEntityHost()); + properties.setEntityHostType(getEntityHostType()); properties.setOwningAvatarID(getOwningAvatarID()); setLastBroadcast(now); // for debug/physics status icons } \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index e119595eb6..8c78dd1cd6 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -64,11 +64,17 @@ const uint64_t MAX_INCOMING_SIMULATION_UPDATE_PERIOD = MAX_OUTGOING_SIMULATION_U class MeshProxyList; -enum class EntityHost { - DOMAIN_ENTITY = 0, - AVATAR_ENTITY, - LOCAL_ENTITY +#ifdef DOMAIN +#undef DOMAIN +#endif + +namespace entity { +enum class HostType { + DOMAIN = 0, + AVATAR, + LOCAL }; +} /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate @@ -484,11 +490,11 @@ public: void setScriptHasFinishedPreload(bool value); bool isScriptPreloadFinished(); - bool isDomainEntity() const { return _entityHost == EntityHost::DOMAIN_ENTITY; } - bool isAvatarEntity() const { return _entityHost == EntityHost::AVATAR_ENTITY; } - bool isLocalEntity() const { return _entityHost == EntityHost::LOCAL_ENTITY; } - EntityHost getEntityHost() const { return _entityHost; } - virtual void setEntityHost(EntityHost entityHost) { _entityHost = entityHost; } + bool isDomainEntity() const { return _hostType == entity::HostType::DOMAIN; } + bool isAvatarEntity() const { return _hostType == entity::HostType::AVATAR; } + bool isLocalEntity() const { return _hostType == entity::HostType::LOCAL; } + entity::HostType getEntityHostType() const { return _hostType; } + virtual void setEntityHostType(entity::HostType hostType) { _hostType = hostType; } // if this entity is an avatar entity, which avatar is it associated with? QUuid getOwningAvatarID() const { return _owningAvatarID; } @@ -683,7 +689,7 @@ protected: QUuid _sourceUUID; /// the server node UUID we came from - EntityHost _entityHost { EntityHost::DOMAIN_ENTITY }; + entity::HostType _hostType { entity::HostType::DOMAIN }; bool _transitingWithAvatar{ false }; QUuid _owningAvatarID; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index bcd53a2420..a477ee50b1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -306,26 +306,26 @@ void EntityItemProperties::setMaterialMappingModeFromString(const QString& mater } } -QString EntityItemProperties::getEntityHostAsString() const { - switch (_entityHost) { - case EntityHost::DOMAIN_ENTITY: +QString EntityItemProperties::getEntityHostTypeAsString() const { + switch (_entityHostType) { + case entity::HostType::DOMAIN: return "domain"; - case EntityHost::AVATAR_ENTITY: + case entity::HostType::AVATAR: return "avatar"; - case EntityHost::LOCAL_ENTITY: + case entity::HostType::LOCAL: return "local"; default: return ""; } } -void EntityItemProperties::setEntityHostFromString(const QString& entityHost) { - if (entityHost == "domain") { - _entityHost = EntityHost::DOMAIN_ENTITY; - } else if (entityHost == "avatar") { - _entityHost = EntityHost::AVATAR_ENTITY; - } else if (entityHost == "local") { - _entityHost = EntityHost::LOCAL_ENTITY; +void EntityItemProperties::setEntityHostTypeFromString(const QString& entityHostType) { + if (entityHostType == "domain") { + _entityHostType = entity::HostType::DOMAIN; + } else if (entityHostType == "avatar") { + _entityHostType = entity::HostType::AVATAR; + } else if (entityHostType == "local") { + _entityHostType = entity::HostType::LOCAL; } } @@ -476,7 +476,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL); - CHECK_PROPERTY_CHANGE(PROP_ENTITY_HOST, entityHost); + CHECK_PROPERTY_CHANGE(PROP_ENTITY_HOST_TYPE, entityHostType); CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID); CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape); @@ -512,16 +512,16 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.EntityType} type - The entity type. You cannot change the type of an entity after it's created. (Though * its value may switch among "Box", "Shape", and "Sphere" depending on changes to * the shape property set for entities of these types.) Read-only. - * @property {EntityHost} entityHost="domain" - How this entity will behave, including if and how it is sent to other people. - * The value can only be set at entity creation by using the entityHost parameter in + * @property {EntityHostType} entityHostType="domain" - How this entity will behave, including if and how it is sent to other people. + * The value can only be set at entity creation by using the entityHostType parameter in * {@link Entities.addEntity}. * @property {boolean} avatarEntity=false - If true then the entity is an avatar entity; An avatar entity follows you to each domain you visit, * rendering at the same world coordinates unless it's parented to your avatar. Value cannot be changed after the entity is created.
- * The value can only be set at entity creation by using the entityHost parameter in + * The value can only be set at entity creation by using the entityHostType parameter in * {@link Entities.addEntity}. clientOnly is an alias. * @property {boolean} localEntity=false - If true then the entity is a local entity; Local entities only render for you and are not sent over the wire. * Value cannot be changed after the entity is created.
- * The value can only be set at entity creation by using the entityHost parameter in + * The value can only be set at entity creation by using the entityHostType parameter in * {@link Entities.addEntity}. * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if avatarEntity is * true, otherwise {@link Uuid|Uuid.NULL}. Read-only. @@ -768,7 +768,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * overlay's ID. * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity - * by setting the entityHost parameter in {@link Entities.addEntity} to "avatar". + * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar". * Material entities render as non-scalable spheres if they don't have their parent set. * @typedef {object} Entities.EntityProperties-Material * @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append ?name to the URL, the @@ -1556,8 +1556,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_DIMENSIONS, localDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ENTITY_HOST, entityHost, getEntityHostAsString()); // 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_GETTER(PROP_ENTITY_HOST_TYPE, entityHostType, getEntityHostTypeAsString()); // 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_CLONE_LIFETIME, cloneLifetime); @@ -1597,13 +1597,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::ClientOnly)) { - properties.setProperty("clientOnly", convertScriptValue(engine, getEntityHost() == EntityHost::AVATAR_ENTITY)); + properties.setProperty("clientOnly", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::AvatarEntity)) { - properties.setProperty("avatarEntity", convertScriptValue(engine, getEntityHost() == EntityHost::AVATAR_ENTITY)); + properties.setProperty("avatarEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::AVATAR)); } if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::LocalEntity)) { - properties.setProperty("localEntity", convertScriptValue(engine, getEntityHost() == EntityHost::LOCAL_ENTITY)); + properties.setProperty("localEntity", convertScriptValue(engine, getEntityHostType() == entity::HostType::LOCAL)); } // FIXME - I don't think these properties are supported any more @@ -1792,7 +1792,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(entityHost, EntityHost); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(entityHostType, EntityHostType); COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); @@ -1958,7 +1958,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(ghostingAllowed); COPY_PROPERTY_IF_CHANGED(filterURL); - COPY_PROPERTY_IF_CHANGED(entityHost); + COPY_PROPERTY_IF_CHANGED(entityHostType); COPY_PROPERTY_IF_CHANGED(owningAvatarID); COPY_PROPERTY_IF_CHANGED(dpi); @@ -3244,7 +3244,7 @@ void EntityItemProperties::markAllChanged() { _ghostingAllowedChanged = true; _filterURLChanged = true; - _entityHostChanged = true; + _entityHostTypeChanged = true; _owningAvatarIDChanged = true; _dpiChanged = true; @@ -3743,8 +3743,8 @@ QList EntityItemProperties::listChangedProperties() { out += "queryAACube"; } - if (entityHostChanged()) { - out += "entityHost"; + if (entityHostTypeChanged()) { + out += "entityHostType"; } if (owningAvatarIDChanged()) { out += "owningAvatarID"; @@ -3819,7 +3819,7 @@ bool EntityItemProperties::getScalesWithParent() const { if (success && parent) { bool avatarAncestor = (parent->getNestableType() == NestableType::Avatar || parent->hasAncestorOfType(NestableType::Avatar)); - scalesWithParent = getEntityHost() == EntityHost::AVATAR_ENTITY && avatarAncestor; + scalesWithParent = getEntityHostType() == entity::HostType::AVATAR && avatarAncestor; } } return scalesWithParent; @@ -3977,11 +3977,11 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setParentJointIndex(-1); setLifetime(getCloneLifetime()); setDynamic(getCloneDynamic()); - if (getEntityHost() != EntityHost::LOCAL_ENTITY) { - setEntityHost(getCloneAvatarEntity() ? EntityHost::AVATAR_ENTITY : EntityHost::DOMAIN_ENTITY); + if (getEntityHostType() != entity::HostType::LOCAL) { + setEntityHostType(getCloneAvatarEntity() ? entity::HostType::AVATAR : entity::HostType::DOMAIN); } else { // Local Entities clone as local entities - setEntityHost(EntityHost::LOCAL_ENTITY); + setEntityHostType(entity::HostType::LOCAL); setCollisionless(true); } setCreated(usecTimestampNow()); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 9e666bca1b..a82e194fef 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -279,7 +279,7 @@ public: DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); - DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST, EntityHost, entityHost, EntityHost, EntityHost::DOMAIN_ENTITY); + DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN); DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI); @@ -589,7 +589,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, GhostingAllowed, ghostingAllowed, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, FilterURL, filterURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostAsString, entityHost, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityHostTypeAsString, entityHostType, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, LastEditedBy, lastEditedBy, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.cpp b/libraries/entities/src/EntityPropertyFlags.cpp index ff95e40fde..4aff36eb71 100644 --- a/libraries/entities/src/EntityPropertyFlags.cpp +++ b/libraries/entities/src/EntityPropertyFlags.cpp @@ -121,7 +121,7 @@ QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { result = f.getHasProperty(PROP_FALLOFF_RADIUS) ? result + "falloffRadius " : result; result = f.getHasProperty(PROP_FLYING_ALLOWED) ? result + "flyingAllowed " : result; result = f.getHasProperty(PROP_GHOSTING_ALLOWED) ? result + "ghostingAllowed " : result; - result = f.getHasProperty(PROP_ENTITY_HOST) ? result + "entityHost " : result; + result = f.getHasProperty(PROP_ENTITY_HOST_TYPE) ? result + "entityHostType " : result; result = f.getHasProperty(PROP_OWNING_AVATAR_ID) ? result + "owningAvatarID " : result; result = f.getHasProperty(PROP_SHAPE) ? result + "shape " : result; result = f.getHasProperty(PROP_DPI) ? result + "dpi " : result; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 7f1d48acdf..e267a46810 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -176,7 +176,7 @@ enum EntityPropertyList { PROP_FLYING_ALLOWED, // can avatars in a zone fly? PROP_GHOSTING_ALLOWED, // can avatars in a zone turn off physics? - PROP_ENTITY_HOST, // doesn't go over wire + PROP_ENTITY_HOST_TYPE, // doesn't go over wire PROP_OWNING_AVATAR_ID, // doesn't go over wire PROP_SHAPE, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index ffd7748c37..825c8350b7 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -470,7 +470,7 @@ void synchronizeEditedGrabProperties(EntityItemProperties& properties, const QSt } -QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, const QString& entityHostString) { +QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, const QString& entityHostTypeString) { PROFILE_RANGE(script_entities, __FUNCTION__); _activityTracking.addedEntityCount++; @@ -479,10 +479,10 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties const auto sessionID = nodeList->getSessionUUID(); EntityItemProperties propertiesWithSimID = properties; - propertiesWithSimID.setEntityHostFromString(entityHostString); - if (propertiesWithSimID.getEntityHost() == EntityHost::AVATAR_ENTITY) { + propertiesWithSimID.setEntityHostTypeFromString(entityHostTypeString); + if (propertiesWithSimID.getEntityHostType() == entity::HostType::AVATAR) { propertiesWithSimID.setOwningAvatarID(sessionID); - } else if (propertiesWithSimID.getEntityHost() == EntityHost::LOCAL_ENTITY) { + } else if (propertiesWithSimID.getEntityHostType() == entity::HostType::LOCAL) { // For now, local entities are always collisionless // TODO: create a separate, local physics simulation that just handles local entities (and MyAvatar?) propertiesWithSimID.setCollisionless(true); @@ -572,7 +572,7 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { bool cloneAvatarEntity = properties.getCloneAvatarEntity(); properties.convertToCloneProperties(entityIDToClone); - if (properties.getEntityHost() == EntityHost::LOCAL_ENTITY) { + if (properties.getEntityHostType() == entity::HostType::LOCAL) { // Local entities are only cloned locally return addEntity(properties, "local"); } else if (cloneAvatarEntity) { @@ -838,9 +838,9 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } // set these to make EntityItemProperties::getScalesWithParent() work correctly - EntityHost entityHost = entity->getEntityHost(); - properties.setEntityHost(entityHost); - if (entityHost == EntityHost::LOCAL_ENTITY) { + entity::HostType entityHostType = entity->getEntityHostType(); + properties.setEntityHostType(entityHostType); + if (entityHostType == entity::HostType::LOCAL) { properties.setCollisionless(true); } properties.setOwningAvatarID(entity->getOwningAvatarID()); @@ -1660,7 +1660,7 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, doTransmit = actor(simulation, entity); _entityTree->entityChanged(entity); if (doTransmit) { - properties.setEntityHost(entity->getEntityHost()); + properties.setEntityHostType(entity->getEntityHostType()); properties.setOwningAvatarID(entity->getOwningAvatarID()); } }); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 78d71ac76b..ff1149fb06 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -245,14 +245,14 @@ public slots: * localLocal entities are not sent over the wire and will only render for you, locally * * - * @typedef {string} EntityHost + * @typedef {string} EntityHostType */ /**jsdoc * Add a new entity with specified properties. * @function Entities.addEntity * @param {Entities.EntityProperties} properties - The properties of the entity to create. - * @param {EntityHost} [entityHost="domain"] - If "avatar" the entity is created as an avatar entity. An avatar entity + * @param {EntityHostType} [entityHostType="domain"] - If "avatar" the entity is created as an avatar entity. An avatar entity * follows you to each domain you visit, rendering at the same world coordinates unless it's parented to your avatar. * If "local", the entity is created as a local entity, which will only render for you and isn't sent over the wire. * Otherwise it is created as a normal entity and sent over the entity server. @@ -266,7 +266,7 @@ public slots: * }); * print("Entity created: " + entityID); */ - Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, const QString& entityHostString); + Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, const QString& entityHostTypeString); /**jsdoc * Add a new entity with specified properties. @@ -276,8 +276,8 @@ public slots: * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. */ Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool avatarEntity = false) { - QString entityHost = avatarEntity ? "avatar" : "domain"; - return addEntity(properties, entityHost); + QString entityHostType = avatarEntity ? "avatar" : "domain"; + return addEntity(properties, entityHostType); } /// temporary method until addEntity can be used from QJSEngine diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1abdd15314..e1330ead12 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -541,7 +541,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti return nullptr; } - if (properties.getEntityHost() == EntityHost::DOMAIN_ENTITY && getIsClient() && + if (properties.getEntityHostType() == entity::HostType::DOMAIN && getIsClient() && !nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() && !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain && !isClone) { return nullptr; @@ -2668,15 +2668,15 @@ bool EntityTree::readFromMap(QVariantMap& map) { entityItemID = EntityItemID(QUuid::createUuid()); } - // Convert old clientOnly bool to new entityHost enum + // Convert old clientOnly bool to new entityHostType enum // (must happen before setOwningAvatarID below) - if (contentVersion < (int)EntityVersion::EntityHosts) { + if (contentVersion < (int)EntityVersion::EntityHostTypes) { if (entityMap.contains("clientOnly")) { - properties.setEntityHost(entityMap["clientOnly"].toBool() ? EntityHost::AVATAR_ENTITY : EntityHost::DOMAIN_ENTITY); + properties.setEntityHostType(entityMap["clientOnly"].toBool() ? entity::HostType::AVATAR : entity::HostType::DOMAIN); } } - if (properties.getEntityHost() == EntityHost::AVATAR_ENTITY) { + if (properties.getEntityHostType() == entity::HostType::AVATAR) { auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); properties.setOwningAvatarID(myNodeID); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 62ff742793..1c58325280 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::EntityHosts); + return static_cast(EntityVersion::EntityHostTypes); 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 284760ea38..83494249c1 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -246,7 +246,7 @@ enum class EntityVersion : PacketVersion { GrabProperties, ScriptGlmVectors, FixedLightSerialization, - EntityHosts + EntityHostTypes }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index aaae31abb1..4b635ef0be 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -595,7 +595,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityTreeElementPointer element = _entity->getElement(); EntityTreePointer tree = element ? element->getTree() : nullptr; - properties.setEntityHost(_entity->getEntityHost()); + properties.setEntityHostType(_entity->getEntityHostType()); properties.setOwningAvatarID(_entity->getOwningAvatarID()); entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties); @@ -610,7 +610,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); newQueryCubeProperties.setLastEdited(properties.getLastEdited()); - newQueryCubeProperties.setEntityHost(entityDescendant->getEntityHost()); + newQueryCubeProperties.setEntityHostType(entityDescendant->getEntityHostType()); newQueryCubeProperties.setOwningAvatarID(entityDescendant->getOwningAvatarID()); entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, From 371c50b2ab90b51f479e3906656a6a8b4d52c31d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 28 Nov 2018 11:01:15 -0800 Subject: [PATCH 08/22] Start work on Pay Out UI --- .../commerce/common/sendAsset/SendAsset.qml | 146 +++++++++++++++++- 1 file changed, 139 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 2d0bb2d87b..2a2369795e 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -269,7 +269,7 @@ Item { RalewaySemiBold { id: sendAssetText; - text: root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:"; + text: root.assetCertID === "" ? "Send Money To:" : "Send \"" + root.assetName + "\" To:"; // Anchors anchors.top: parent.top; anchors.topMargin: 26; @@ -370,6 +370,50 @@ Item { } } + Item { + id: authorizedScriptButton; + // Anchors + anchors.top: nearbyButton.bottom; + anchors.topMargin: 32; + anchors.horizontalCenter: parent.horizontalCenter; + height: connectionButton.height; + width: connectionButton.width; + + Image { + anchors.top: parent.top; + source: "./images/nearby.svg"; + height: 70; + width: parent.width; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignTop; + mipmap: true; + } + + RalewaySemiBold { + text: "Authorized Script"; + // Anchors + anchors.bottom: parent.bottom; + height: 15; + width: parent.width; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + sendAssetStep.referrer = "authorizedScript"; + sendAssetStep.selectedRecipientNodeID = ""; + + root.nextActiveView = "sendAssetStep"; + } + } + } + HifiControlsUit.Button { id: backButton_sendAssetHome; visible: parentAppNavBarHeight === 0; @@ -860,7 +904,7 @@ Item { id: sendAssetStep; z: 996; - property string referrer; // either "connections", "nearby", or "payIn" + property string referrer; // either "connections", "nearby", "payIn", or "authorizedScript" property string selectedRecipientNodeID; property string selectedRecipientDisplayName; property string selectedRecipientUserName; @@ -872,7 +916,8 @@ Item { RalewaySemiBold { id: sendAssetText_sendAssetStep; - text: sendAssetStep.referrer === "payIn" && root.assetCertID !== "" ? "Send \"" + root.assetName + "\":" : + text: ((sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "authorizedScript") && + root.assetCertID !== "") ? "Send \"" + root.assetName + "\":" : (root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:"); // Anchors anchors.top: parent.top; @@ -901,12 +946,13 @@ Item { RalewaySemiBold { id: sendToText_sendAssetStep; - text: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:"; + text: sendAssetStep.referrer === "authorizedScript" ? "Script Secret:" : + (root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:"; // Anchors anchors.top: parent.top; anchors.left: parent.left; anchors.bottom: parent.bottom; - width: 90; + width: paintedWidth; // Text size size: 18; // Style @@ -915,8 +961,10 @@ Item { } RecipientDisplay { + visible: sendAssetStep.referrer !== "authorizedScript"; anchors.top: parent.top; anchors.left: sendToText_sendAssetStep.right; + anchors.leftMargin: 16; anchors.right: changeButton.left; anchors.rightMargin: 12; height: parent.height; @@ -929,6 +977,71 @@ Item { multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn"; } + Item { + id: scriptSecretContainer; + visible: sendAssetStep.referrer === "authorizedScript"; + anchors.top: parent.top; + anchors.left: sendToText_sendAssetStep.right; + anchors.right: parent.right; + height: parent.height; + + RalewaySemiBold { + id: scriptSecretHelp; + text: "[?]"; + // Anchors + anchors.left: parent.left; + anchors.leftMargin: 8; + anchors.verticalCenter: parent.verticalCenter; + height: 30; + width: paintedWidth; + // Text size + size: 18; + // Style + color: hifi.colors.blueAccent; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.color = hifi.colors.blueHighlight; + } + onExited: { + parent.color = hifi.colors.blueAccent; + } + onClicked: { + lightboxPopup.titleText = "Script Secret"; + lightboxPopup.bodyText = "This alphanumeric text string will be used to ensure " + + "that only your scripts have access to the asset that you are sending. Keep it private!"; + lightboxPopup.button1text = "CLOSE"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + } + lightboxPopup.visible = true; + } + } + } + + HifiControlsUit.TextField { + id: scriptSecretTextField; + text: generateRandomSecret(); + colorScheme: root.assetCertID === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: scriptSecretHelp.right; + anchors.leftMargin: 16; + anchors.right: parent.right; + height: 50; + // Style + activeFocusOnPress: true; + activeFocusOnTab: true; + + validator: RegExpValidator { regExp: /^[a-zA-Z0-9]+$/ } + + onAccepted: { + optionalMessage.focus = true; + } + } + } + // "CHANGE" button HifiControlsUit.Button { id: changeButton; @@ -939,7 +1052,7 @@ Item { height: 35; width: 100; text: "CHANGE"; - visible: sendAssetStep.referrer !== "payIn"; + visible: sendAssetStep.referrer !== "payIn" && sendAssetStep.referrer !== "authorizedScript"; onClicked: { if (sendAssetStep.referrer === "connections") { root.nextActiveView = "chooseRecipientConnection"; @@ -1263,6 +1376,8 @@ Item { root.assetCertID, parseInt(amountTextField.text), optionalMessage.text); + } else if (sendAssetStep.referrer === "authorizedScript") { + console.log("ZRF HERE: SENDING TO AUTHORIZED SCRIPT"); } } } @@ -1867,6 +1982,17 @@ Item { sendAssetStep.referrer = ""; } + function generateRandomSecret() { + var randomSecret = ""; + var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for (var i = 0; i < 10; i++) { + randomSecret += possibleCharacters.charAt(Math.floor(Math.random() * possibleCharacters.length)); + } + + return randomSecret; + } + // // Function Name: fromScript() // @@ -1908,9 +2034,15 @@ Item { sendAssetStep.referrer = "payIn"; sendAssetStep.selectedRecipientNodeID = ""; sendAssetStep.selectedRecipientDisplayName = "Determined by script:"; - sendAssetStep.selectedRecipientUserName = message.username; + sendAssetStep.selectedRecipientUserName = message.username || ""; optionalMessage.text = message.message || "No Message Provided"; + if (sendAssetStep.selectedRecipientUserName === "") { + console.log("SendAsset: Script didn't specify a recipient username!"); + sendAssetHome.visible = false; + return; + } + root.nextActiveView = "sendAssetStep"; break; case 'inspectionCertificate_resetCert': From bc39dbba8b38b80f6023f5e47dd75d4248c6cce6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 29 Nov 2018 12:44:30 -0800 Subject: [PATCH 09/22] Finish out the feature --- .../commerce/common/sendAsset/SendAsset.qml | 297 +++++++++++++++--- .../common/sendAsset/images/clipboard.svg | 1 + interface/src/commerce/Ledger.cpp | 16 + interface/src/commerce/Ledger.h | 4 + interface/src/commerce/QmlCommerce.cpp | 16 + interface/src/commerce/QmlCommerce.h | 2 + 6 files changed, 291 insertions(+), 45 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 2a2369795e..36113077f5 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -36,6 +36,8 @@ Item { property bool isCurrentlySendingAsset: false; property string assetName: ""; property string assetCertID: ""; + property string secret: ""; + property string authorizationID: ""; property string sendingPubliclyEffectImage; property var http; property var listModelName; @@ -108,6 +110,27 @@ Item { } } + onAuthorizeAssetTransferResult: { + if (!root.visible) { + return; + } + + root.isCurrentlySendingAsset = false; + + if (result.status === 'success') { + root.authorizationID = result.data.authorization_id; + authorizationIDText.text = root.authorizationID; + root.secret = result.data.secret; + secretText.text = root.secret + if (scriptSecretTextField.text !== root.secret) { + console.log("SendAsset: Returned secret doesn't match client-generated secret!"); + } + root.nextActiveView = 'paymentSuccess'; + } else { + root.nextActiveView = 'paymentFailure'; + } + } + onCertificateInfoResult: { if (result.status !== 'success') { console.log("Failed to get certificate info", result.data.message); @@ -408,6 +431,7 @@ Item { onClicked: { sendAssetStep.referrer = "authorizedScript"; sendAssetStep.selectedRecipientNodeID = ""; + scriptSecretTextField.text = generateRandomSecret(); root.nextActiveView = "sendAssetStep"; } @@ -1022,7 +1046,6 @@ Item { HifiControlsUit.TextField { id: scriptSecretTextField; - text: generateRandomSecret(); colorScheme: root.assetCertID === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light; // Anchors anchors.verticalCenter: parent.verticalCenter; @@ -1034,8 +1057,6 @@ Item { activeFocusOnPress: true; activeFocusOnTab: true; - validator: RegExpValidator { regExp: /^[a-zA-Z0-9]+$/ } - onAccepted: { optionalMessage.focus = true; } @@ -1377,7 +1398,10 @@ Item { parseInt(amountTextField.text), optionalMessage.text); } else if (sendAssetStep.referrer === "authorizedScript") { - console.log("ZRF HERE: SENDING TO AUTHORIZED SCRIPT"); + Commerce.authorizeAssetTransfer(scriptSecretTextField.text || "", + root.assetCertID, + parseInt(amountTextField.text) || 1, + optionalMessage.text) } } } @@ -1449,18 +1473,24 @@ Item { Rectangle { anchors.top: parent.top; - anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125; + anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 125; anchors.left: parent.left; - anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50; + anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 50; anchors.right: parent.right; - anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50; + anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 50; anchors.bottom: parent.bottom; - anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125; + anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 125; color: "#FFFFFF"; RalewaySemiBold { id: paymentSentText; - text: root.assetCertID === "" ? "Payment Sent" : (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent"); + text: root.assetCertID === "" ? (sendAssetStep.referrer === "authorizedScript" ? "Payment Authorized" : "Payment Sent") : + (sendAssetStep.referrer === "authorizedScript" ? "Item Transfer Authorized" : + (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent")); // Anchors anchors.top: parent.top; anchors.topMargin: 26; @@ -1498,6 +1528,8 @@ Item { onClicked: { if (sendAssetStep.referrer === "payIn") { sendToScript({method: "closeSendAsset"}); + } else if (sendAssetStep.referrer === "authorizedScript") { + showDidYouCopyLightbox(); } else { root.nextActiveView = "sendAssetHome"; resetSendAssetData(); @@ -1517,38 +1549,176 @@ Item { anchors.leftMargin: 20; anchors.right: parent.right; anchors.rightMargin: 20; - height: 80; + height: childrenRect.height; - RalewaySemiBold { - id: sendToText_paymentSuccess; - text: "Sent To:"; - // Anchors + Item { + id: sendToScriptContainer_paymentSuccess; + visible: sendAssetStep.referrer === "authorizedScript"; anchors.top: parent.top; anchors.left: parent.left; - anchors.bottom: parent.bottom; - width: 90; - // Text size - size: 18; - // Style - color: hifi.colors.baseGray; - verticalAlignment: Text.AlignVCenter; - } - - RecipientDisplay { - anchors.top: parent.top; - anchors.left: sendToText_paymentSuccess.right; anchors.right: parent.right; - height: parent.height; - textColor: hifi.colors.blueAccent; + height: childrenRect.height; - displayName: sendAssetStep.selectedRecipientDisplayName; - userName: sendAssetStep.selectedRecipientUserName; - profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ? - sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : ""; - multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn"; + RalewaySemiBold { + id: authorizationIDLabel; + text: "Authorization ID:"; + // Anchors + anchors.left: parent.left; + anchors.top: authorizationIDText.top; + width: paintedWidth; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + } + + RalewayRegular { + id: authorizationIDText; + text: root.authorizationID; + anchors.top: parent.top; + anchors.left: authorizationIDLabel.right; + anchors.leftMargin: 16; + anchors.right: authorizationIDClipboardButton.left; + anchors.rightMargin: 16; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + wrapMode: Text.WrapAnywhere; + } + + Image { + id: authorizationIDClipboardButton; + source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project + fillMode: Image.PreserveAspectFit; + // Anchors + anchors.right: parent.right; + anchors.top: authorizationIDText.top; + height: 40; + width: height; + + MouseArea { + anchors.fill: parent; + onClicked: { + Window.copyToClipboard(root.authorizationID); + authorizationIDText.text = "Copied to Clipboard!\n"; + authorizationIDClipboardTimer.start(); + } + } + } + + Timer { + id: authorizationIDClipboardTimer; + interval: 2000; + repeat: false; + onTriggered: { + authorizationIDText.text = root.authorizationID; + } + } + + RalewaySemiBold { + id: secretLabel; + text: "Secret:"; + // Anchors + anchors.left: parent.left; + anchors.top: secretText.top; + width: authorizationIDLabel.width; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + } + + RalewayRegular { + id: secretText; + text: root.secret; + anchors.top: authorizationIDText.bottom; + anchors.topMargin: 16; + anchors.left: secretLabel.right; + anchors.leftMargin: 16; + anchors.right: secretClipboardButton.left; + anchors.rightMargin: 16; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + wrapMode: Text.WrapAnywhere; + } + + Image { + id: secretClipboardButton; + source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project + fillMode: Image.PreserveAspectFit; + // Anchors + anchors.right: parent.right; + anchors.top: secretText.top; + height: 40; + width: height; + + MouseArea { + anchors.fill: parent; + onClicked: { + Window.copyToClipboard(root.secret); + secretText.text = "Copied to Clipboard!\n"; + secretClipboardTimer.start(); + } + } + } + + Timer { + id: secretClipboardTimer; + interval: 2000; + repeat: false; + onTriggered: { + secretText.text = root.secret; + } + } } - } - + + Item { + id: sendToRecipientContainer_paymentSuccess; + visible: !sendToScriptContainer_paymentSuccess.visible; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + + RalewaySemiBold { + id: sendToText_paymentSuccess; + text: "Sent To:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + } + + RecipientDisplay { + anchors.top: parent.top; + anchors.left: sendToText_paymentSuccess.right; + anchors.right: parent.right; + height: parent.height; + textColor: hifi.colors.blueAccent; + + displayName: sendAssetStep.selectedRecipientDisplayName; + userName: sendAssetStep.selectedRecipientUserName; + profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ? + sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : ""; + multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn"; + } + } + } Item { id: giftContainer_paymentSuccess; @@ -1563,7 +1733,8 @@ Item { RalewaySemiBold { id: gift_paymentSuccess; - text: sendAssetStep.referrer === "payIn" ? "Item:" : "Gift:"; + text: sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "authorizedScript" ? + "Item:" : "Gift:"; // Anchors anchors.top: parent.top; anchors.left: parent.left; @@ -1681,6 +1852,8 @@ Item { onClicked: { if (sendAssetStep.referrer === "payIn") { sendToScript({method: "closeSendAsset"}); + } else if (sendAssetStep.referrer === "authorizedScript") { + showDidYouCopyLightbox(); } else { root.nextActiveView = "sendAssetHome"; resetSendAssetData(); @@ -1714,13 +1887,17 @@ Item { Rectangle { anchors.top: parent.top; - anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 150; + anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 150; anchors.left: parent.left; - anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50; + anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 50; anchors.right: parent.right; - anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50; + anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 50; anchors.bottom: parent.bottom; - anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 300; + anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || + sendAssetStep.referrer === "authorizedScript" ? 15 : 300; color: "#FFFFFF"; RalewaySemiBold { @@ -1772,8 +1949,9 @@ Item { RalewaySemiBold { id: paymentFailureDetailText; - text: "The recipient you specified was unable to receive your " + - (root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift.")); + text: sendAssetStep.referrer === "authorizedScript" ? "The server was unable to handle your request. Please try again later." : + ("The recipient you specified was unable to receive your " + + (root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift."))); anchors.top: paymentFailureText.bottom; anchors.topMargin: 20; anchors.left: parent.left; @@ -1791,7 +1969,8 @@ Item { Item { id: sendToContainer_paymentFailure; - visible: root.assetCertID === "" || sendAssetStep.referrer === "payIn"; + visible: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") && + sendAssetStep.referrer !== "authorizedScript"; anchors.top: paymentFailureDetailText.bottom; anchors.topMargin: 8; anchors.left: parent.left; @@ -1833,7 +2012,8 @@ Item { Item { id: amountContainer_paymentFailure; visible: root.assetCertID === ""; - anchors.top: sendToContainer_paymentFailure.bottom; + anchors.top: sendToContainer_paymentFailure.visible ? + sendToContainer_paymentFailure.bottom : paymentFailureDetailText.bottom; anchors.topMargin: 16; anchors.left: parent.left; anchors.leftMargin: 20; @@ -1954,6 +2134,11 @@ Item { root.assetCertID, parseInt(amountTextField.text), optionalMessage.text); + } else if (sendAssetStep.referrer === "authorizedScript") { + Commerce.authorizeAssetTransfer(scriptSecretTextField.text || "", + root.assetCertID, + parseInt(amountTextField.text) || 1, + optionalMessage.text) } } } @@ -1983,16 +2168,38 @@ Item { } function generateRandomSecret() { + var RANDOM_SECRET_LENGTH = 25; var randomSecret = ""; var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (var i = 0; i < 10; i++) { + for (var i = 0; i < RANDOM_SECRET_LENGTH; i++) { randomSecret += possibleCharacters.charAt(Math.floor(Math.random() * possibleCharacters.length)); } return randomSecret; } + function showDidYouCopyLightbox() { + lightboxPopup.titleText = "Close Confirmation"; + lightboxPopup.bodyText = "Did you copy your Authorization ID and your Script Secret?\n\n" + + "You won't be able to see your Authorization ID or your Script Secret once " + + "you close this window."; + lightboxPopup.button1text = "GO BACK"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + } + lightboxPopup.button2text = "I'M ALL SET"; + lightboxPopup.button2method = function() { + lightboxPopup.visible = false; + root.nextActiveView = "sendAssetHome"; + resetSendAssetData(); + if (root.assetName !== "") { + sendSignalToParent({method: "closeSendAsset"}); + } + } + lightboxPopup.visible = true; + } + // // Function Name: fromScript() // diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg b/interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg new file mode 100644 index 0000000000..798fdaaab1 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/images/clipboard.svg @@ -0,0 +1 @@ +Created by Bieutuong Bonfrom the Noun Project \ No newline at end of file diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index b10c9647a0..fb177ddc82 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -63,6 +63,7 @@ Handler(balance) Handler(inventory) Handler(transferAssetToNode) Handler(transferAssetToUsername) +Handler(authorizeAssetTransfer) Handler(alreadyOwned) Handler(availableUpdates) Handler(updateItem) @@ -428,6 +429,7 @@ void Ledger::transferAssetToUsername(const QString& hfc_key, const QString& user transaction["username"] = username; transaction["quantity"] = amount; transaction["message"] = optionalMessage; + transaction["place_name"] = DependencyManager::get()->getPlaceName(); if (!certificateID.isEmpty()) { transaction["certificate_id"] = certificateID; } @@ -440,6 +442,20 @@ void Ledger::transferAssetToUsername(const QString& hfc_key, const QString& user } } +void Ledger::authorizeAssetTransfer(const QString& hfc_key, const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage) { + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["secret"] = secret; + transaction["quantity"] = amount; + transaction["message"] = optionalMessage; + if (!certificateID.isEmpty()) { + transaction["certificate_id"] = certificateID; + } + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + signedSend("transaction", transactionString, hfc_key, "authorize", "authorizeAssetTransferSuccess", "authorizeAssetTransferFailure"); +} + void Ledger::alreadyOwned(const QString& marketplaceId) { auto wallet = DependencyManager::get(); QString endpoint = "already_owned"; diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 715d6337ad..700cbe2c4b 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -36,6 +36,7 @@ public: void certificateInfo(const QString& certificateId); void transferAssetToNode(const QString& hfc_key, const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage); void transferAssetToUsername(const QString& hfc_key, const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage); + void authorizeAssetTransfer(const QString& hfc_key, const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage); void alreadyOwned(const QString& marketplaceId); void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10); void updateItem(const QString& hfc_key, const QString& certificate_id); @@ -59,6 +60,7 @@ signals: void certificateInfoResult(QJsonObject result); void transferAssetToNodeResult(QJsonObject result); void transferAssetToUsernameResult(QJsonObject result); + void authorizeAssetTransferResult(QJsonObject result); void alreadyOwnedResult(QJsonObject result); void availableUpdatesResult(QJsonObject result); void updateItemResult(QJsonObject result); @@ -86,6 +88,8 @@ public slots: void transferAssetToNodeFailure(QNetworkReply* reply); void transferAssetToUsernameSuccess(QNetworkReply* reply); void transferAssetToUsernameFailure(QNetworkReply* reply); + void authorizeAssetTransferSuccess(QNetworkReply* reply); + void authorizeAssetTransferFailure(QNetworkReply* reply); void alreadyOwnedSuccess(QNetworkReply* reply); void alreadyOwnedFailure(QNetworkReply* reply); void availableUpdatesSuccess(QNetworkReply* reply); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 566f7ba324..aab053484b 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -38,6 +38,7 @@ QmlCommerce::QmlCommerce() { connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); connect(ledger.data(), &Ledger::transferAssetToNodeResult, this, &QmlCommerce::transferAssetToNodeResult); connect(ledger.data(), &Ledger::transferAssetToUsernameResult, this, &QmlCommerce::transferAssetToUsernameResult); + connect(ledger.data(), &Ledger::authorizeAssetTransferResult, this, &QmlCommerce::authorizeAssetTransferResult); connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult); connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult); @@ -246,6 +247,21 @@ void QmlCommerce::transferAssetToUsername(const QString& username, ledger->transferAssetToUsername(key, username, certificateID, amount, optionalMessage); } +void QmlCommerce::authorizeAssetTransfer(const QString& secret, + const QString& certificateID, + const int& amount, + const QString& optionalMessage) { + auto ledger = DependencyManager::get(); + auto wallet = DependencyManager::get(); + QStringList keys = wallet->listPublicKeys(); + if (keys.count() == 0) { + QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } }; + return emit authorizeAssetTransferResult(result); + } + QString key = keys[0]; + ledger->authorizeAssetTransfer(key, secret, certificateID, amount, optionalMessage); +} + void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) { if (!certificateID.isEmpty()) { auto ledger = DependencyManager::get(); diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index c5fbdaf4a4..e22b540624 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -51,6 +51,7 @@ signals: void transferAssetToNodeResult(QJsonObject result); void transferAssetToUsernameResult(QJsonObject result); + void authorizeAssetTransferResult(QJsonObject result); void contentSetChanged(const QString& contentSetHref); @@ -84,6 +85,7 @@ protected: Q_INVOKABLE void transferAssetToNode(const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage); + Q_INVOKABLE void authorizeAssetTransfer(const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID); From c959dd7024d0a7f4ef841b6884fd945ed3ccaaab Mon Sep 17 00:00:00 2001 From: birarda Date: Mon, 3 Dec 2018 14:56:46 -0800 Subject: [PATCH 10/22] release avatar entity and traits locks sooner to avoid deadlock --- libraries/avatars/src/AvatarData.cpp | 38 ++++++++++++------- libraries/avatars/src/ClientTraitsHandler.cpp | 17 ++++++--- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d9d4b57c31..d4357fac49 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2832,35 +2832,47 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { qCDebug(avatars) << "discard suspect AvatarEntityData with size =" << avatarEntityData.size(); return; } + + std::vector deletedEntityIDs; + QList updatedEntityIDs; + _avatarEntitiesLock.withWriteLock([&] { if (_avatarEntityData != avatarEntityData) { + // keep track of entities that were attached to this avatar but no longer are AvatarEntityIDs previousAvatarEntityIDs = QSet::fromList(_avatarEntityData.keys()); _avatarEntityData = avatarEntityData; setAvatarEntityDataChanged(true); + deletedEntityIDs.reserve(previousAvatarEntityIDs.size()); + foreach (auto entityID, previousAvatarEntityIDs) { if (!_avatarEntityData.contains(entityID)) { _avatarEntityDetached.insert(entityID); - - if (_clientTraitsHandler) { - // we have a client traits handler, so we flag this removed entity as deleted - // so that changes are sent next frame - _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); - } + deletedEntityIDs.push_back(entityID); } } - if (_clientTraitsHandler) { - // if we have a client traits handler, flag any updated or created entities - // so that we send changes for them next frame - foreach (auto entityID, _avatarEntityData.keys()) { - _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); - } - } + updatedEntityIDs = _avatarEntityData.keys(); } }); + + if (_clientTraitsHandler) { + // we have a client traits handler + + // flag removed entities as deleted so that changes are sent next frame + for (auto& deletedEntityID : deletedEntityIDs) { + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, deletedEntityID); + } + + // flag any updated or created entities so that we send changes for them next frame + for (auto& entityID : updatedEntityIDs) { + _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); + } + } + + } AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() { diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index a301341a8e..cbc8e93745 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -66,7 +66,7 @@ void ClientTraitsHandler::resetForNewMixer() { } void ClientTraitsHandler::sendChangedTraitsToMixer() { - Lock lock(_traitLock); + std::unique_lock lock(_traitLock); if (hasChangedTraits() || _shouldPerformInitialSend) { // we have at least one changed trait to send @@ -90,6 +90,14 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { _traitStatuses.reset(); _hasChangedTraits = false; + // if this was an initial send of all traits, consider it completed + bool initialSend = _shouldPerformInitialSend; + _shouldPerformInitialSend = false; + + // we can release the lock here since we've taken a copy of statuses + // and will setup the packet using the information in the copy + lock.unlock(); + auto simpleIt = traitStatusesCopy.simpleCBegin(); while (simpleIt != traitStatusesCopy.simpleCEnd()) { // because the vector contains all trait types (for access using trait type as index) @@ -111,12 +119,12 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { auto instancedIt = traitStatusesCopy.instancedCBegin(); while (instancedIt != traitStatusesCopy.instancedCEnd()) { for (auto& instanceIDValuePair : instancedIt->instances) { - if ((_shouldPerformInitialSend && instanceIDValuePair.value != Deleted) + if ((initialSend && instanceIDValuePair.value != Deleted) || instanceIDValuePair.value == Updated) { // this is a changed trait we need to send or we haven't send out trait information yet // ask the owning avatar to pack it _owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); - } else if (!_shouldPerformInitialSend && instanceIDValuePair.value == Deleted) { + } else if (!initialSend && instanceIDValuePair.value == Deleted) { // pack delete for this trait instance AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); @@ -127,9 +135,6 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { } nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer); - - // if this was an initial send of all traits, consider it completed - _shouldPerformInitialSend = false; } } From ab184c5c2b7adadeb42c4e8c390e8b6cc5e69797 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 3 Dec 2018 15:16:06 -0800 Subject: [PATCH 11/22] Authorized Script to Coupon --- .../commerce/common/sendAsset/SendAsset.qml | 126 +++++++++--------- .../common/sendAsset/images/coupon.svg | 10 ++ 2 files changed, 73 insertions(+), 63 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/common/sendAsset/images/coupon.svg diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 36113077f5..7c2116f77b 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -36,7 +36,7 @@ Item { property bool isCurrentlySendingAsset: false; property string assetName: ""; property string assetCertID: ""; - property string secret: ""; + property string couponID: ""; property string authorizationID: ""; property string sendingPubliclyEffectImage; property var http; @@ -120,9 +120,9 @@ Item { if (result.status === 'success') { root.authorizationID = result.data.authorization_id; authorizationIDText.text = root.authorizationID; - root.secret = result.data.secret; - secretText.text = root.secret - if (scriptSecretTextField.text !== root.secret) { + root.couponID = result.data.secret; + couponIDText.text = root.couponID + if (couponIDTextField.text !== root.couponID) { console.log("SendAsset: Returned secret doesn't match client-generated secret!"); } root.nextActiveView = 'paymentSuccess'; @@ -394,7 +394,7 @@ Item { } Item { - id: authorizedScriptButton; + id: createCouponButton; // Anchors anchors.top: nearbyButton.bottom; anchors.topMargin: 32; @@ -404,7 +404,7 @@ Item { Image { anchors.top: parent.top; - source: "./images/nearby.svg"; + source: "./images/coupon.svg"; height: 70; width: parent.width; fillMode: Image.PreserveAspectFit; @@ -414,7 +414,7 @@ Item { } RalewaySemiBold { - text: "Authorized Script"; + text: "Create Coupon"; // Anchors anchors.bottom: parent.bottom; height: 15; @@ -429,9 +429,9 @@ Item { MouseArea { anchors.fill: parent; onClicked: { - sendAssetStep.referrer = "authorizedScript"; + sendAssetStep.referrer = "createCoupon"; sendAssetStep.selectedRecipientNodeID = ""; - scriptSecretTextField.text = generateRandomSecret(); + couponIDTextField.text = generateRandomCouponID(); root.nextActiveView = "sendAssetStep"; } @@ -928,7 +928,7 @@ Item { id: sendAssetStep; z: 996; - property string referrer; // either "connections", "nearby", "payIn", or "authorizedScript" + property string referrer; // either "connections", "nearby", "payIn", or "createCoupon" property string selectedRecipientNodeID; property string selectedRecipientDisplayName; property string selectedRecipientUserName; @@ -940,7 +940,7 @@ Item { RalewaySemiBold { id: sendAssetText_sendAssetStep; - text: ((sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "authorizedScript") && + text: ((sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "createCoupon") && root.assetCertID !== "") ? "Send \"" + root.assetName + "\":" : (root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:"); // Anchors @@ -970,7 +970,7 @@ Item { RalewaySemiBold { id: sendToText_sendAssetStep; - text: sendAssetStep.referrer === "authorizedScript" ? "Script Secret:" : + text: sendAssetStep.referrer === "createCoupon" ? "Coupon ID:" : (root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:"; // Anchors anchors.top: parent.top; @@ -985,7 +985,7 @@ Item { } RecipientDisplay { - visible: sendAssetStep.referrer !== "authorizedScript"; + visible: sendAssetStep.referrer !== "createCoupon"; anchors.top: parent.top; anchors.left: sendToText_sendAssetStep.right; anchors.leftMargin: 16; @@ -1002,15 +1002,15 @@ Item { } Item { - id: scriptSecretContainer; - visible: sendAssetStep.referrer === "authorizedScript"; + id: couponIDContainer; + visible: sendAssetStep.referrer === "createCoupon"; anchors.top: parent.top; anchors.left: sendToText_sendAssetStep.right; anchors.right: parent.right; height: parent.height; RalewaySemiBold { - id: scriptSecretHelp; + id: couponIDHelp; text: "[?]"; // Anchors anchors.left: parent.left; @@ -1032,9 +1032,9 @@ Item { parent.color = hifi.colors.blueAccent; } onClicked: { - lightboxPopup.titleText = "Script Secret"; + lightboxPopup.titleText = "Coupon ID"; lightboxPopup.bodyText = "This alphanumeric text string will be used to ensure " + - "that only your scripts have access to the asset that you are sending. Keep it private!"; + "that only you can redeem the coupon for the asset that you are sending. Keep it private!"; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { lightboxPopup.visible = false; @@ -1045,11 +1045,11 @@ Item { } HifiControlsUit.TextField { - id: scriptSecretTextField; + id: couponIDTextField; colorScheme: root.assetCertID === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light; // Anchors anchors.verticalCenter: parent.verticalCenter; - anchors.left: scriptSecretHelp.right; + anchors.left: couponIDHelp.right; anchors.leftMargin: 16; anchors.right: parent.right; height: 50; @@ -1073,7 +1073,7 @@ Item { height: 35; width: 100; text: "CHANGE"; - visible: sendAssetStep.referrer !== "payIn" && sendAssetStep.referrer !== "authorizedScript"; + visible: sendAssetStep.referrer !== "payIn" && sendAssetStep.referrer !== "createCoupon"; onClicked: { if (sendAssetStep.referrer === "connections") { root.nextActiveView = "chooseRecipientConnection"; @@ -1397,8 +1397,8 @@ Item { root.assetCertID, parseInt(amountTextField.text), optionalMessage.text); - } else if (sendAssetStep.referrer === "authorizedScript") { - Commerce.authorizeAssetTransfer(scriptSecretTextField.text || "", + } else if (sendAssetStep.referrer === "createCoupon") { + Commerce.authorizeAssetTransfer(couponIDTextField.text || "", root.assetCertID, parseInt(amountTextField.text) || 1, optionalMessage.text) @@ -1474,22 +1474,22 @@ Item { Rectangle { anchors.top: parent.top; anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 125; + sendAssetStep.referrer === "createCoupon" ? 15 : 125; anchors.left: parent.left; anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 50; + sendAssetStep.referrer === "createCoupon" ? 15 : 50; anchors.right: parent.right; anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 50; + sendAssetStep.referrer === "createCoupon" ? 15 : 50; anchors.bottom: parent.bottom; anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 125; + sendAssetStep.referrer === "createCoupon" ? 15 : 125; color: "#FFFFFF"; RalewaySemiBold { id: paymentSentText; - text: root.assetCertID === "" ? (sendAssetStep.referrer === "authorizedScript" ? "Payment Authorized" : "Payment Sent") : - (sendAssetStep.referrer === "authorizedScript" ? "Item Transfer Authorized" : + text: root.assetCertID === "" ? (sendAssetStep.referrer === "createCoupon" ? "Payment Authorized" : "Payment Sent") : + (sendAssetStep.referrer === "createCoupon" ? "Item Transfer Authorized" : (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent")); // Anchors anchors.top: parent.top; @@ -1528,7 +1528,7 @@ Item { onClicked: { if (sendAssetStep.referrer === "payIn") { sendToScript({method: "closeSendAsset"}); - } else if (sendAssetStep.referrer === "authorizedScript") { + } else if (sendAssetStep.referrer === "createCoupon") { showDidYouCopyLightbox(); } else { root.nextActiveView = "sendAssetHome"; @@ -1553,7 +1553,7 @@ Item { Item { id: sendToScriptContainer_paymentSuccess; - visible: sendAssetStep.referrer === "authorizedScript"; + visible: sendAssetStep.referrer === "createCoupon"; anchors.top: parent.top; anchors.left: parent.left; anchors.right: parent.right; @@ -1620,11 +1620,11 @@ Item { } RalewaySemiBold { - id: secretLabel; - text: "Secret:"; + id: couponIDLabel; + text: "Coupon ID:"; // Anchors anchors.left: parent.left; - anchors.top: secretText.top; + anchors.top: couponIDText.top; width: authorizationIDLabel.width; // Text size size: 18; @@ -1634,13 +1634,13 @@ Item { } RalewayRegular { - id: secretText; - text: root.secret; + id: couponIDText; + text: root.couponID; anchors.top: authorizationIDText.bottom; anchors.topMargin: 16; - anchors.left: secretLabel.right; + anchors.left: couponIDLabel.right; anchors.leftMargin: 16; - anchors.right: secretClipboardButton.left; + anchors.right: couponIDClipboardButton.left; anchors.rightMargin: 16; // Text size size: 18; @@ -1652,31 +1652,31 @@ Item { } Image { - id: secretClipboardButton; + id: couponIDClipboardButton; source: "images/clipboard.svg"; // clipboard by Bieutuong Bon from the Noun Project fillMode: Image.PreserveAspectFit; // Anchors anchors.right: parent.right; - anchors.top: secretText.top; + anchors.top: couponIDText.top; height: 40; width: height; MouseArea { anchors.fill: parent; onClicked: { - Window.copyToClipboard(root.secret); - secretText.text = "Copied to Clipboard!\n"; - secretClipboardTimer.start(); + Window.copyToClipboard(root.couponID); + couponIDText.text = "Copied to Clipboard!\n"; + couponIDClipboardTimer.start(); } } } Timer { - id: secretClipboardTimer; + id: couponIDClipboardTimer; interval: 2000; repeat: false; onTriggered: { - secretText.text = root.secret; + couponIDText.text = root.couponID; } } } @@ -1733,7 +1733,7 @@ Item { RalewaySemiBold { id: gift_paymentSuccess; - text: sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "authorizedScript" ? + text: sendAssetStep.referrer === "payIn" || sendAssetStep.referrer === "createCoupon" ? "Item:" : "Gift:"; // Anchors anchors.top: parent.top; @@ -1852,7 +1852,7 @@ Item { onClicked: { if (sendAssetStep.referrer === "payIn") { sendToScript({method: "closeSendAsset"}); - } else if (sendAssetStep.referrer === "authorizedScript") { + } else if (sendAssetStep.referrer === "createCoupon") { showDidYouCopyLightbox(); } else { root.nextActiveView = "sendAssetHome"; @@ -1888,16 +1888,16 @@ Item { Rectangle { anchors.top: parent.top; anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 150; + sendAssetStep.referrer === "createCoupon" ? 15 : 150; anchors.left: parent.left; anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 50; + sendAssetStep.referrer === "createCoupon" ? 15 : 50; anchors.right: parent.right; anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 50; + sendAssetStep.referrer === "createCoupon" ? 15 : 50; anchors.bottom: parent.bottom; anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" || - sendAssetStep.referrer === "authorizedScript" ? 15 : 300; + sendAssetStep.referrer === "createCoupon" ? 15 : 300; color: "#FFFFFF"; RalewaySemiBold { @@ -1949,7 +1949,7 @@ Item { RalewaySemiBold { id: paymentFailureDetailText; - text: sendAssetStep.referrer === "authorizedScript" ? "The server was unable to handle your request. Please try again later." : + text: sendAssetStep.referrer === "createCoupon" ? "The server was unable to handle your request. Please try again later." : ("The recipient you specified was unable to receive your " + (root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift."))); anchors.top: paymentFailureText.bottom; @@ -1970,7 +1970,7 @@ Item { Item { id: sendToContainer_paymentFailure; visible: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") && - sendAssetStep.referrer !== "authorizedScript"; + sendAssetStep.referrer !== "createCoupon"; anchors.top: paymentFailureDetailText.bottom; anchors.topMargin: 8; anchors.left: parent.left; @@ -2134,8 +2134,8 @@ Item { root.assetCertID, parseInt(amountTextField.text), optionalMessage.text); - } else if (sendAssetStep.referrer === "authorizedScript") { - Commerce.authorizeAssetTransfer(scriptSecretTextField.text || "", + } else if (sendAssetStep.referrer === "createCoupon") { + Commerce.authorizeAssetTransfer(couponIDTextField.text || "", root.assetCertID, parseInt(amountTextField.text) || 1, optionalMessage.text) @@ -2167,22 +2167,22 @@ Item { sendAssetStep.referrer = ""; } - function generateRandomSecret() { - var RANDOM_SECRET_LENGTH = 25; - var randomSecret = ""; + function generateRandomCouponID() { + var RANDOM_COUPON_ID_LENGTH = 25; + var randomCouponID = ""; var possibleCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (var i = 0; i < RANDOM_SECRET_LENGTH; i++) { - randomSecret += possibleCharacters.charAt(Math.floor(Math.random() * possibleCharacters.length)); + for (var i = 0; i < RANDOM_COUPON_ID_LENGTH; i++) { + randomCouponID += possibleCharacters.charAt(Math.floor(Math.random() * possibleCharacters.length)); } - return randomSecret; + return randomCouponID; } function showDidYouCopyLightbox() { lightboxPopup.titleText = "Close Confirmation"; - lightboxPopup.bodyText = "Did you copy your Authorization ID and your Script Secret?\n\n" + - "You won't be able to see your Authorization ID or your Script Secret once " + + lightboxPopup.bodyText = "Did you copy your Authorization ID and your Coupon ID?\n\n" + + "You won't be able to see your Authorization ID or your Coupon ID once " + "you close this window."; lightboxPopup.button1text = "GO BACK"; lightboxPopup.button1method = function() { diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/images/coupon.svg b/interface/resources/qml/hifi/commerce/common/sendAsset/images/coupon.svg new file mode 100644 index 0000000000..2b7c052589 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/images/coupon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From 0c64d1227a5c7c57eca72dec9c027b039df0564c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 4 Dec 2018 16:41:03 -0800 Subject: [PATCH 12/22] fix vive crash --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 99c861871d..62f1720814 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -485,6 +485,7 @@ bool OpenVrDisplayPlugin::internalActivate() { _submitCanvas->doneCurrent(); }); } + _submitCanvas->moveToThread(_submitThread.get()); } return Parent::internalActivate(); From 58396b845fc4a9733d9d0bb43d6bbb3d72a15336 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 4 Dec 2018 17:32:50 -0800 Subject: [PATCH 13/22] move back to plugin thread (cherry picked from commit 98d3f6a9d1191031cda1ff040e69093ef6824f1c) --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 62f1720814..11ef222172 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -319,6 +319,7 @@ public: glBindVertexArray(0); glDeleteVertexArrays(1, &_vao); _canvas->doneCurrent(); + _canvas->moveToThread(_plugin.thread()); } void update(const CompositeInfo& newCompositeInfo) { _queue.push(newCompositeInfo); } From 02307d2308f56f21754d7a121d635d4062d7b6cb Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 5 Dec 2018 16:34:53 -0800 Subject: [PATCH 14/22] Secret to Coupon ID --- .../qml/hifi/commerce/common/sendAsset/SendAsset.qml | 4 ++-- interface/src/commerce/Ledger.cpp | 4 ++-- interface/src/commerce/Ledger.h | 2 +- interface/src/commerce/QmlCommerce.cpp | 4 ++-- interface/src/commerce/QmlCommerce.h | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 7c2116f77b..3125ad1ee6 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -120,10 +120,10 @@ Item { if (result.status === 'success') { root.authorizationID = result.data.authorization_id; authorizationIDText.text = root.authorizationID; - root.couponID = result.data.secret; + root.couponID = result.data.coupon_id; couponIDText.text = root.couponID if (couponIDTextField.text !== root.couponID) { - console.log("SendAsset: Returned secret doesn't match client-generated secret!"); + console.log("SendAsset: Returned coupon ID doesn't match client-generated coupon ID!"); } root.nextActiveView = 'paymentSuccess'; } else { diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index fb177ddc82..8bb01f6389 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -442,10 +442,10 @@ void Ledger::transferAssetToUsername(const QString& hfc_key, const QString& user } } -void Ledger::authorizeAssetTransfer(const QString& hfc_key, const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage) { +void Ledger::authorizeAssetTransfer(const QString& hfc_key, const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage) { QJsonObject transaction; transaction["public_key"] = hfc_key; - transaction["secret"] = secret; + transaction["coupon_id"] = couponID; transaction["quantity"] = amount; transaction["message"] = optionalMessage; if (!certificateID.isEmpty()) { diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 700cbe2c4b..2e18f34c8d 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -36,7 +36,7 @@ public: void certificateInfo(const QString& certificateId); void transferAssetToNode(const QString& hfc_key, const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage); void transferAssetToUsername(const QString& hfc_key, const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage); - void authorizeAssetTransfer(const QString& hfc_key, const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage); + void authorizeAssetTransfer(const QString& hfc_key, const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage); void alreadyOwned(const QString& marketplaceId); void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10); void updateItem(const QString& hfc_key, const QString& certificate_id); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index aab053484b..00acd40e70 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -247,7 +247,7 @@ void QmlCommerce::transferAssetToUsername(const QString& username, ledger->transferAssetToUsername(key, username, certificateID, amount, optionalMessage); } -void QmlCommerce::authorizeAssetTransfer(const QString& secret, +void QmlCommerce::authorizeAssetTransfer(const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage) { @@ -259,7 +259,7 @@ void QmlCommerce::authorizeAssetTransfer(const QString& secret, return emit authorizeAssetTransferResult(result); } QString key = keys[0]; - ledger->authorizeAssetTransfer(key, secret, certificateID, amount, optionalMessage); + ledger->authorizeAssetTransfer(key, couponID, certificateID, amount, optionalMessage); } void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) { diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index e22b540624..ad21899ebf 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -85,7 +85,7 @@ protected: Q_INVOKABLE void transferAssetToNode(const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage); - Q_INVOKABLE void authorizeAssetTransfer(const QString& secret, const QString& certificateID, const int& amount, const QString& optionalMessage); + Q_INVOKABLE void authorizeAssetTransfer(const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage); Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID); From 3160339551dda631170d4fc7f1b82aa03b8e036d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Dec 2018 10:50:50 -0800 Subject: [PATCH 15/22] Improve Create properties for particles + materials Sizing of input fields is now flexible. Material scale field labels are no longer obscured. Material tooltips are corrected. --- .../system/assets/data/createAppTooltips.json | 2 +- scripts/system/html/css/edit-style.css | 30 +++++++++------- scripts/system/html/js/draggableNumber.js | 12 +++---- scripts/system/html/js/entityProperties.js | 35 ++++++++++--------- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/assets/data/createAppTooltips.json index 720d4537ee..3a25402588 100644 --- a/scripts/system/assets/data/createAppTooltips.json +++ b/scripts/system/assets/data/createAppTooltips.json @@ -267,7 +267,7 @@ "jsPropertyName": "parentMaterialName" }, "selectSubmesh": { - "tooltip": "If enabled, \"Select Submesh\" property will show up, otherwise \"Material Name to Replace\" will be shown.", + "tooltip": "If enabled, \"Submesh to Replace\" property will show up, otherwise \"Material to Replace\" will be shown.", "skipJSProperty": true }, "priority": { diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 415d8e567f..d7d88ce91e 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -598,6 +598,7 @@ div.section[collapsed="true"], div.section[collapsed="true"] > .section-header { .triple-label { text-transform: uppercase; + text-align: center; padding: 6px 0; } @@ -605,6 +606,10 @@ div.section[collapsed="true"], div.section[collapsed="true"] > .section-header { margin-right: 10px; } +.triple-item.rgb.fstuple { + display: block !important; +} + .section-header[collapsed="true"] { margin-bottom: -21px; } @@ -911,14 +916,17 @@ div.refresh input[type="button"] { clear: both; } +.draggable-number-container { + flex: 0 1 124px; +} .draggable-number { position: relative; -} -.draggable-number div { height: 28px; - width: 124px; + flex: 0 1 124px; } -.draggable-number.text { + +.draggable-number .text { + position: absolute; display: inline-block; color: #afafaf; background-color: #252525; @@ -930,11 +938,12 @@ div.refresh input[type="button"] { width: 100%; line-height: 2; box-sizing: border-box; + z-index: 1; } -.draggable-number.text:hover { +.draggable-number .text:hover { cursor: ew-resize; } -.draggable-number span { +.draggable-number .left-arrow, .draggable-number .right-arrow { position: absolute; display: inline-block; font-family: HiFi-Glyphs; @@ -944,12 +953,12 @@ div.refresh input[type="button"] { .draggable-number span:hover { cursor: default; } -.draggable-number.left-arrow { +.draggable-number .left-arrow { top: 3px; left: 0px; transform: rotate(180deg); } -.draggable-number.right-arrow { +.draggable-number .right-arrow { top: 3px; right: 0px; } @@ -1514,6 +1523,7 @@ input.rename-entity { width: 258px; min-height: 20px; padding: 5px; + z-index: 100; } .create-app-tooltip .create-app-tooltip-description { @@ -1629,10 +1639,6 @@ input.number-slider { flex-flow: column; } -.flex-column + .flex-column { - padding-left: 50px; -} - .flex-center { align-items: center; } diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 951b123e67..3995b846be 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -156,8 +156,8 @@ DraggableNumber.prototype = { this.elDiv = document.createElement('div'); this.elDiv.className = "draggable-number"; - this.elText = document.createElement('label'); - this.elText.className = "draggable-number text"; + this.elText = document.createElement('span'); + this.elText.className = "text"; this.elText.innerText = " "; this.elText.style.visibility = "visible"; this.elText.addEventListener("mousedown", this.onMouseDown); @@ -165,15 +165,15 @@ DraggableNumber.prototype = { this.elLeftArrow = document.createElement('span'); this.elRightArrow = document.createElement('span'); - this.elLeftArrow.className = 'draggable-number left-arrow'; + this.elLeftArrow.className = 'left-arrow'; this.elLeftArrow.innerHTML = 'D'; this.elLeftArrow.addEventListener("click", this.onStepDown); - this.elRightArrow.className = 'draggable-number right-arrow'; + this.elRightArrow.className = 'right-arrow'; this.elRightArrow.innerHTML = 'D'; this.elRightArrow.addEventListener("click", this.onStepUp); this.elInput = document.createElement('input'); - this.elInput.className = "draggable-number input"; + this.elInput.className = "input"; this.elInput.setAttribute("type", "number"); if (this.min !== undefined) { this.elInput.setAttribute("min", this.min); @@ -190,8 +190,8 @@ DraggableNumber.prototype = { this.elInput.addEventListener("focus", this.showInput.bind(this)); this.elDiv.appendChild(this.elLeftArrow); + this.elDiv.appendChild(this.elText); this.elDiv.appendChild(this.elInput); this.elDiv.appendChild(this.elRightArrow); - this.elDiv.appendChild(this.elText); } }; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 14ed2b77e3..ff760e3a4a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -556,22 +556,24 @@ const GROUPS = [ { id: "save", label: "Save Material Data", className: "black", onClick: saveMaterialData } ], propertyID: "materialData", }, + { + label: "Select Submesh", + type: "bool", + propertyID: "selectSubmesh", + }, { label: "Submesh to Replace", type: "number", min: 0, step: 1, propertyID: "submeshToReplace", + indentedLabel: true, }, { - label: "Material Name to Replace", + label: "Material to Replace", type: "string", propertyID: "materialNameToReplace", - }, - { - label: "Select Submesh", - type: "bool", - propertyID: "selectSubmesh", + indentedLabel: true, }, { label: "Priority", @@ -582,7 +584,7 @@ const GROUPS = [ { label: "Material Position", type: "vec2", - vec2Type: "xy", + vec2Type: "xyz", min: 0, max: 1, step: 0.1, @@ -593,11 +595,11 @@ const GROUPS = [ { label: "Material Scale", type: "vec2", - vec2Type: "wh", + vec2Type: "xyz", min: 0, step: 0.1, decimals: 4, - subLabels: [ "width", "height" ], + subLabels: [ "x", "y" ], propertyID: "materialMappingScale", }, { @@ -1858,7 +1860,7 @@ function createNumberProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "draggable-number"; + elProperty.className += " draggable-number-container"; let dragStartFunction = createDragStartFunction(property); let dragEndFunction = createDragEndFunction(property); @@ -1937,7 +1939,7 @@ function createColorProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - elProperty.className = "rgb fstuple"; + elProperty.className += " rgb fstuple"; let elColorPicker = document.createElement('div'); elColorPicker.className = "color-picker"; @@ -1978,6 +1980,7 @@ function createColorProperty(property, elProperty) { color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { + console.log("Showing"); $(colorPickerID).attr('active', 'true'); // The original color preview within the picker needs to be updated on show because // prior to the picker being shown we don't have access to the selections' starting color. @@ -2879,20 +2882,20 @@ function loaded() { for (let i = 0; i < propertyData.properties.length; ++i) { let innerPropertyData = propertyData.properties[i]; - let elWrapper = createElementFromHTML('
'); + let elWrapper = createElementFromHTML('
'); + elProperty.appendChild(elWrapper); let propertyID = innerPropertyData.propertyID; let propertyName = innerPropertyData.propertyName !== undefined ? innerPropertyData.propertyName : propertyID; let propertyElementID = "property-" + propertyID; propertyElementID = propertyElementID.replace('.', '-'); - elWrapper.appendChild(createElementFromHTML(`
${innerPropertyData.label}
`)); - elProperty.appendChild(elWrapper); - - let property = createProperty(innerPropertyData, propertyElementID, propertyName, propertyID, elWrapper.childNodes[0]); + let property = createProperty(innerPropertyData, propertyElementID, propertyName, propertyID, elWrapper); property.isParticleProperty = group.id.includes("particles"); property.elContainer = elContainer; property.spaceMode = propertySpaceMode; + + elWrapper.appendChild(createElementFromHTML(`
${innerPropertyData.label}
`)); if (property.type !== 'placeholder') { properties[propertyID] = property; From a2af50a9d0b7a82c7c993d1801b2c5ee0d797afe Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 6 Dec 2018 11:07:26 -0800 Subject: [PATCH 16/22] Show pre-auth'd transactions in Recent Activity before redemption --- .../qml/hifi/commerce/wallet/WalletHome.qml | 8 ++++++-- interface/src/commerce/Ledger.cpp | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 4c8e1e6ca5..cf293a06df 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -270,9 +270,11 @@ Item { model: transactionHistoryModel; delegate: Item { width: parent.width; - height: (model.transaction_type === "pendingCount" && model.count !== 0) ? 40 : ((model.status === "confirmed" || model.status === "invalidated") ? transactionText.height + 30 : 0); + height: (model.transaction_type === "pendingCount" && model.count !== 0) ? 40 : + (transactionContainer.visible ? transactionText.height + 30 : 0); Item { + id: pendingCountContainer; visible: model.transaction_type === "pendingCount" && model.count !== 0; anchors.top: parent.top; anchors.left: parent.left; @@ -291,7 +293,9 @@ Item { } Item { - visible: model.transaction_type !== "pendingCount" && (model.status === "confirmed" || model.status === "invalidated"); + id: transactionContainer; + visible: model.transaction_type !== "pendingCount" && + (model.status === "confirmed" || model.status === "invalidated"); anchors.top: parent.top; anchors.left: parent.left; width: parent.width; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 8bb01f6389..d72d896638 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -204,6 +204,7 @@ QString transactionString(const QJsonObject& valueObject) { int sentMoney = valueObject["sent_money"].toInt(); int receivedMoney = valueObject["received_money"].toInt(); int dateInteger = valueObject["created_at"].toInt(); + QString transactionType = valueObject["transaction_type"].toString(); QString message = valueObject["message"].toString(); QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC)); QString result; @@ -211,8 +212,12 @@ QString transactionString(const QJsonObject& valueObject) { if (sentCerts <= 0 && receivedCerts <= 0 && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) { // this is an hfc transfer. if (sentMoney > 0) { - QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); - result += QString("Money sent to %1").arg(recipient); + if (transactionType == "escrow") { + result += QString("Money transferred to coupon"); + } else { + QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); + result += QString("Money sent to %1").arg(recipient); + } } else { QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString()); result += QString("Money from %1").arg(sender); @@ -227,8 +232,12 @@ QString transactionString(const QJsonObject& valueObject) { ) { // this is a non-HFC asset transfer. if (sentCerts > 0) { - QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); - result += QString("Gift sent to %1").arg(recipient); + if (transactionType == "escrow") { + result += QString("Item transferred to coupon"); + } else { + QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); + result += QString("Gift sent to %1").arg(recipient); + } } else { QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString()); result += QString("Gift from %1").arg(sender); From 8620a5a4c3649903a4f50c19684e95f6749f17bb Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 6 Dec 2018 13:07:59 -0800 Subject: [PATCH 17/22] CR changes --- scripts/system/html/js/listView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index b19ba7e0cb..eec3f833ad 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -242,7 +242,7 @@ ListView.prototype = { resize: function() { if (!this.elTableBody || !this.elTableScroll) { - debugPrint("ListView.resize - no valid table body or table scroll element"); + console.log("ListView.resize - no valid table body or table scroll element"); return; } this.preResizeFunction(); @@ -284,7 +284,7 @@ ListView.prototype = { initialize: function() { if (!this.elTableBody || !this.elTableScroll) { - debugPrint("ListView.initialize - no valid table body or table scroll element"); + console.log("ListView.initialize - no valid table body or table scroll element"); return; } From 6d40bc9c2f0fdccccf2b76c499469fda98916192 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 29 Nov 2018 19:41:56 +0100 Subject: [PATCH 18/22] userData error message continues popup fix --- scripts/system/html/css/edit-style.css | 24 +++++ scripts/system/html/js/entityProperties.js | 103 +++++++++++++-------- 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 415d8e567f..641b9bb5fe 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1663,3 +1663,27 @@ input.number-slider { .collapse-icon { cursor: pointer; } + +#property-userData-editor.error { + border: 2px solid red; +} + +#property-userData-editorStatus { + color: white; + background-color: red; + padding: 5px; + display: none; + cursor: pointer; +} + +#property-materialData-editor.error { + border: 2px solid red; +} + +#property-materialData-editorStatus { + color: white; + background-color: red; + padding: 5px; + display: none; + cursor: pointer; +} diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index dc304c6803..bc93a1a9b7 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1823,7 +1823,7 @@ function createStringProperty(property, elProperty) { type="text" ${propertyData.placeholder ? 'placeholder="' + propertyData.placeholder + '"' : ''} ${propertyData.readOnly ? 'readonly' : ''}> - `) + `); elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); @@ -2359,31 +2359,41 @@ function saveUserData() { saveJSONUserData(true); } +function setJSONError(property, isError) { + $("#property-"+ property + "-editor").toggleClass('error', isError); + var $propertyUserDataEditorStatus = $("#property-"+ property + "-editorStatus"); + $propertyUserDataEditorStatus.css('display', isError ? 'block' : 'none'); + $propertyUserDataEditorStatus.text(isError ? 'Invalid JSON code - look for red X in your code' : ''); +} + function setUserDataFromEditor(noUpdate) { let json = null; + let errorFound = false; try { json = editor.get(); } catch (e) { - alert('Invalid JSON code - look for red X in your code ', +e); + errorFound = true; } - if (json === null) { + + setJSONError('userData', errorFound); + + if (json !== null) { return; + } + + let text = editor.getText(); + if (noUpdate) { + EventBridge.emitWebEvent( + JSON.stringify({ + id: lastEntityID, + type: "saveUserData", + properties: { + userData: text + } + }) + ); } else { - let text = editor.getText(); - if (noUpdate === true) { - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "saveUserData", - properties: { - userData: text - } - }) - ); - return; - } else { - updateProperty('userData', text, false); - } + updateProperty('userData', text, false); } } @@ -2505,9 +2515,10 @@ function hideUserDataSaved() { function showStaticUserData() { if (editor !== null) { - $('#property-userData-static').show(); - $('#property-userData-static').css('height', $('#property-userData-editor').height()); - $('#property-userData-static').text(editor.getText()); + let $propertyUserDataStatic = $('#property-userData-static'); + $propertyUserDataStatic.show(); + $propertyUserDataStatic.css('height', $('#property-userData-editor').height()); + $propertyUserDataStatic.text(editor.getText()); } } @@ -2528,6 +2539,7 @@ function getEditorJSON() { function deleteJSONEditor() { if (editor !== null) { + setJSONError('userData', false); editor.destroy(); editor = null; } @@ -2578,29 +2590,31 @@ function saveMaterialData() { function setMaterialDataFromEditor(noUpdate) { let json = null; + let errorFound = false; try { json = materialEditor.get(); } catch (e) { - alert('Invalid JSON code - look for red X in your code ', +e); + errorFound = true; } + + setJSONError('materialData', errorFound); + if (json === null) { return; + } + let text = materialEditor.getText(); + if (noUpdate) { + EventBridge.emitWebEvent( + JSON.stringify({ + id: lastEntityID, + type: "saveMaterialData", + properties: { + materialData: text + } + }) + ); } else { - let text = materialEditor.getText(); - if (noUpdate === true) { - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "saveMaterialData", - properties: { - materialData: text - } - }) - ); - return; - } else { - updateProperty('materialData', text, false); - } + updateProperty('materialData', text, false); } } @@ -2667,9 +2681,10 @@ function hideMaterialDataSaved() { function showStaticMaterialData() { if (materialEditor !== null) { - $('#property-materialData-static').show(); - $('#property-materialData-static').css('height', $('#property-materialData-editor').height()); - $('#property-materialData-static').text(materialEditor.getText()); + let $propertyMaterialDataStatic = $('#property-materialData-static'); + $propertyMaterialDataStatic.show(); + $propertyMaterialDataStatic.css('height', $('#property-materialData-editor').height()); + $propertyMaterialDataStatic.text(materialEditor.getText()); } } @@ -2955,7 +2970,7 @@ function loaded() { let elServerScriptError = document.getElementById("property-serverScripts-error"); let elServerScriptStatus = document.getElementById("property-serverScripts-status"); elServerScriptError.value = data.errorInfo; - // If we just set elServerScriptError's diplay to block or none, we still end up with + // If we just set elServerScriptError's display to block or none, we still end up with // it's parent contributing 21px bottom padding even when elServerScriptError is display:none. // So set it's parent to block or none elServerScriptError.parentElement.style.display = data.errorInfo ? "block" : "none"; @@ -3314,12 +3329,15 @@ function loaded() { elStaticUserData.setAttribute("id", userDataElementID + "-static"); let elUserDataEditor = document.createElement('div'); elUserDataEditor.setAttribute("id", userDataElementID + "-editor"); + let elUserDataEditorStatus = document.createElement('div'); + elUserDataEditorStatus.setAttribute("id", userDataElementID + "-editorStatus"); let elUserDataSaved = document.createElement('span'); elUserDataSaved.setAttribute("id", userDataElementID + "-saved"); elUserDataSaved.innerText = "Saved!"; elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elUserDataSaved); elDiv.insertBefore(elStaticUserData, elUserData); elDiv.insertBefore(elUserDataEditor, elUserData); + elDiv.insertBefore(elUserDataEditorStatus, elUserData); // Material Data let materialDataProperty = properties["materialData"]; @@ -3330,12 +3348,15 @@ function loaded() { elStaticMaterialData.setAttribute("id", materialDataElementID + "-static"); let elMaterialDataEditor = document.createElement('div'); elMaterialDataEditor.setAttribute("id", materialDataElementID + "-editor"); + let elMaterialDataEditorStatus = document.createElement('div'); + elMaterialDataEditorStatus.setAttribute("id", materialDataElementID + "-editorStatus"); let elMaterialDataSaved = document.createElement('span'); elMaterialDataSaved.setAttribute("id", materialDataElementID + "-saved"); elMaterialDataSaved.innerText = "Saved!"; elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elMaterialDataSaved); elDiv.insertBefore(elStaticMaterialData, elMaterialData); elDiv.insertBefore(elMaterialDataEditor, elMaterialData); + elDiv.insertBefore(elMaterialDataEditorStatus, elMaterialData); // Special Property Callbacks let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); From 2771e2862e15e54b6cfa7edf696ef0e18d2f58f7 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 29 Nov 2018 19:55:34 +0100 Subject: [PATCH 19/22] add missing material data json error clear on delete --- scripts/system/html/js/entityProperties.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index bc93a1a9b7..ed03d62e70 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -2705,6 +2705,7 @@ function getMaterialEditorJSON() { function deleteJSONMaterialEditor() { if (materialEditor !== null) { + setJSONError('materialData', false); materialEditor.destroy(); materialEditor = null; } From 4f2289b347db6342006d41f98fdc89604f7525e1 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 6 Dec 2018 23:42:12 +0100 Subject: [PATCH 20/22] CR fixes --- scripts/system/html/js/entityProperties.js | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index ed03d62e70..6f8f2ef4bc 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1429,13 +1429,13 @@ const TEXTURE_ELEMENTS = { const JSON_EDITOR_ROW_DIV_INDEX = 2; -var elGroups = {}; -var properties = {}; -var colorPickers = {}; -var particlePropertyUpdates = {}; -var selectedEntityProperties; -var lastEntityID = null; -var createAppTooltip = new CreateAppTooltip(); +let elGroups = {}; +let properties = {}; +let colorPickers = {}; +let particlePropertyUpdates = {}; +let selectedEntityProperties; +let lastEntityID = null; +let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; function createElementFromHTML(htmlString) { @@ -1689,7 +1689,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) } } -var particleSyncDebounce = _.debounce(function () { +let particleSyncDebounce = _.debounce(function () { updateProperties(particlePropertyUpdates); particlePropertyUpdates = {}; }, DEBOUNCE_TIMEOUT); @@ -2361,7 +2361,7 @@ function saveUserData() { function setJSONError(property, isError) { $("#property-"+ property + "-editor").toggleClass('error', isError); - var $propertyUserDataEditorStatus = $("#property-"+ property + "-editorStatus"); + let $propertyUserDataEditorStatus = $("#property-"+ property + "-editorStatus"); $propertyUserDataEditorStatus.css('display', isError ? 'block' : 'none'); $propertyUserDataEditorStatus.text(isError ? 'Invalid JSON code - look for red X in your code' : ''); } @@ -2377,7 +2377,7 @@ function setUserDataFromEditor(noUpdate) { setJSONError('userData', errorFound); - if (json !== null) { + if (errorFound) { return; } @@ -2452,7 +2452,7 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r updateProperties(propertyUpdate, false); } -var editor = null; +let editor = null; function createJSONEditor() { let container = document.getElementById("property-userData-editor"); @@ -2545,7 +2545,7 @@ function deleteJSONEditor() { } } -var savedJSONTimer = null; +let savedJSONTimer = null; function saveJSONUserData(noUpdate) { setUserDataFromEditor(noUpdate); @@ -2599,7 +2599,7 @@ function setMaterialDataFromEditor(noUpdate) { setJSONError('materialData', errorFound); - if (json === null) { + if (errorFound) { return; } let text = materialEditor.getText(); @@ -2618,7 +2618,7 @@ function setMaterialDataFromEditor(noUpdate) { } } -var materialEditor = null; +let materialEditor = null; function createJSONMaterialEditor() { let container = document.getElementById("property-materialData-editor"); @@ -2711,7 +2711,7 @@ function deleteJSONMaterialEditor() { } } -var savedMaterialJSONTimer = null; +let savedMaterialJSONTimer = null; function saveJSONMaterialData(noUpdate) { setMaterialDataFromEditor(noUpdate); From 62a06fe3834fe91248c0d970daf90c2a9b648916 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 29 Nov 2018 11:10:57 -0800 Subject: [PATCH 21/22] Update build docs --- BUILD.md | 20 ++++++++++++++++---- BUILD_WIN.md | 23 +++++------------------ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/BUILD.md b/BUILD.md index 00b17743e9..25bbc89951 100644 --- a/BUILD.md +++ b/BUILD.md @@ -13,7 +13,7 @@ ### CMake External Project Dependencies -These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required. +These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required. - [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83 - [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8 - [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac) @@ -22,7 +22,7 @@ These dependencies need not be installed manually. They are automatically downlo - [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3 - [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3 - [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/): 4.3 -- [vcpkg](https://github.com/highfidelity/vcpkg): +- [vcpkg](https://github.com/highfidelity/vcpkg): - [VHACD](https://github.com/virneo/v-hacd) - [zlib](http://www.zlib.net/): 1.28 (Win32 only) - [nvtt](https://github.com/highfidelity/nvidia-texture-tools): 2.1.1 (customized) @@ -48,6 +48,18 @@ The path it needs to be set to will depend on where and how Qt5 was installed. e export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.10.1/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake +#### Vcpkg + +Hifi uses vcpkg to download and build dependencies. +You do not need to install vcpkg. + +Building the dependencies can be lengthy and the resulting files will be stored in your OS temp directory. +However, those files can potentially get cleaned up by the OS, so in order to avoid this and having to redo the lengthy build step, you can set the following environment variable: + +export HIFI_VCPKG_BASE=/path/to/directory + +Where /path/to/directory is the path to a directory where you wish the build files to get stored. + #### Generating build files Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean. @@ -80,7 +92,7 @@ In the examples below the variable $NAME would be replaced by the name of the de ### Optional Components -#### Build Options +#### Build Options The following build options can be used when running CMake @@ -89,7 +101,7 @@ The following build options can be used when running CMake * BUILD_TESTS * BUILD_TOOLS -#### Developer Build Options +#### Developer Build Options * USE_GLES * DISABLE_UI diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 073b048911..a81fca5900 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -19,7 +19,7 @@ If you do not wish to use the Python installation bundled with Visual Studio, yo ### Step 2. Installing CMake -Download and install the latest version of CMake 3.9. +Download and install the latest version of CMake 3.9. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.9 Version page](https://cmake.org/files/v3.9/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted. @@ -35,20 +35,7 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables * Set "Variable name": `QT_CMAKE_PREFIX_PATH` * Set "Variable value": `C:\Qt\5.10.1\msvc2017_64\lib\cmake` -### Step 5. Installing [vcpkg](https://github.com/Microsoft/vcpkg) - - * Clone the VCPKG [repository](https://github.com/Microsoft/vcpkg) - * Follow the instructions in the [readme](https://github.com/Microsoft/vcpkg/blob/master/README.md) to bootstrap vcpkg - * Note, you may need to do these in a _Developer Command Prompt_ - * Set an environment variable VCPKG_ROOT to the location of the cloned repository - * Close and re-open any command prompts after setting the environment variable so that they will pick up the change - -### Step 6. Installing OpenSSL via vcpkg - - * In the vcpkg directory, install the 64 bit OpenSSL package with the command `.\vcpkg install openssl:x64-windows` - * Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl` - -### Step 7. Running CMake to Generate Build Files +### Step 5. Running CMake to Generate Build Files Run Command Prompt from Start and run the following commands: ``` @@ -58,9 +45,9 @@ cd build cmake .. -G "Visual Studio 15 Win64" ``` -Where `%HIFI_DIR%` is the directory for the highfidelity repository. +Where `%HIFI_DIR%` is the directory for the highfidelity repository. -### Step 8. Making a Build +### Step 6. Making a Build Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio. @@ -68,7 +55,7 @@ Change the Solution Configuration (menu ribbon under the menu bar, next to the g Run from the menu bar `Build > Build Solution`. -### Step 9. Testing Interface +### Step 7. Testing Interface Create another environment variable (see Step #4) * Set "Variable name": `_NO_DEBUG_HEAP` From 1a3708c49dca4479429f1ca98cca4f3ad4c601f9 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 6 Dec 2018 16:14:28 -0800 Subject: [PATCH 22/22] better drag drop fix --- scripts/system/html/js/utils.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/js/utils.js b/scripts/system/html/js/utils.js index fe96e8b79e..d61b4d1762 100644 --- a/scripts/system/html/js/utils.js +++ b/scripts/system/html/js/utils.js @@ -11,13 +11,17 @@ function disableDragDrop() { document.addEventListener("drop", function(event) { event.preventDefault(); - event.dataTransfer.effectAllowed = "none"; - event.dataTransfer.dropEffect = "none"; - }, false); + }); document.addEventListener("dragover", function(event) { - event.preventDefault(); event.dataTransfer.effectAllowed = "none"; event.dataTransfer.dropEffect = "none"; + event.preventDefault(); + }); + + document.addEventListener("dragenter", function(event) { + event.dataTransfer.effectAllowed = "none"; + event.dataTransfer.dropEffect = "none"; + event.preventDefault(); }, false); }