EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {
    EntityItemProperties results;
    if (_entityTree) {
        _entityTree->withReadLock([&] {
            EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
            if (entity) {
                results = entity->getProperties(desiredProperties);

                // TODO: improve sitting points and naturalDimensions in the future,
                //       for now we've included the old sitting points model behavior for entity types that are models
                //        we've also added this hack for setting natural dimensions of models
                if (entity->getType() == EntityTypes::Model) {
                    const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
                    if (geometry) {
                        results.setSittingPoints(geometry->sittingPoints);
                        Extents meshExtents = geometry->getUnscaledMeshExtents();
                        results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
                        results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
                    }
                }

            }
        });
    }

    return results;
}
void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type,
        EntityTreePointer entityTree,
        EntityItemID entityItemID,
        const EntityItemProperties& properties) {
    if (!_shouldSend) {
        return; // bail early
    }

    if (properties.getOwningAvatarID() != _myAvatar->getID()) {
        return; // don't send updates for someone else's avatarEntity
    }

    assert(properties.getClientOnly());

    // this is an avatar-based entity.  update our avatar-data rather than sending to the entity-server
    assert(_myAvatar);

    if (!entityTree) {
        qCDebug(entities) << "EntityEditPacketSender::queueEditEntityMessage null entityTree.";
        return;
    }
    EntityItemPointer entity = entityTree->findEntityByEntityItemID(entityItemID);
    if (!entity) {
        qCDebug(entities) << "EntityEditPacketSender::queueEditEntityMessage can't find entity.";
        return;
    }

    // the properties that get serialized into the avatar identity packet should be the entire set
    // rather than just the ones being edited.
    EntityItemProperties entityProperties = entity->getProperties();
    entityProperties.merge(properties);

    QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties);
    QVariant variantProperties = scriptProperties.toVariant();
    QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties);

    // the ID of the parent/avatar changes from session to session.  use a special UUID to indicate the avatar
    QJsonObject jsonObject = jsonProperties.object();
    if (QUuid(jsonObject["parentID"].toString()) == _myAvatar->getID()) {
        jsonObject["parentID"] = AVATAR_SELF_ID.toString();
    }
    jsonProperties = QJsonDocument(jsonObject);

    QByteArray binaryProperties = jsonProperties.toBinaryData();
    _myAvatar->updateAvatarEntity(entityItemID, binaryProperties);

    entity->setLastBroadcast(usecTimestampNow());
    return;
}
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {
    EntityItemProperties results;
    if (_entityTree) {
        _entityTree->withReadLock([&] {
            EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
            if (entity) {
                if (desiredProperties.getHasProperty(PROP_POSITION) ||
                    desiredProperties.getHasProperty(PROP_ROTATION) ||
                    desiredProperties.getHasProperty(PROP_LOCAL_POSITION) ||
                    desiredProperties.getHasProperty(PROP_LOCAL_ROTATION)) {
                    // if we are explicitly getting position or rotation, we need parent information to make sense of them.
                    desiredProperties.setHasProperty(PROP_PARENT_ID);
                    desiredProperties.setHasProperty(PROP_PARENT_JOINT_INDEX);
                }

                if (desiredProperties.isEmpty()) {
                    // these are left out of EntityItem::getEntityProperties so that localPosition and localRotation
                    // don't end up in json saves, etc.  We still want them here, though.
                    EncodeBitstreamParams params; // unknown
                    desiredProperties = entity->getEntityProperties(params);
                    desiredProperties.setHasProperty(PROP_LOCAL_POSITION);
                    desiredProperties.setHasProperty(PROP_LOCAL_ROTATION);
                 }

                results = entity->getProperties(desiredProperties);

                // TODO: improve sitting points and naturalDimensions in the future,
                //       for now we've included the old sitting points model behavior for entity types that are models
                //        we've also added this hack for setting natural dimensions of models
                if (entity->getType() == EntityTypes::Model) {
                    const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
                    if (geometry) {
                        results.setSittingPoints(geometry->sittingPoints);
                        Extents meshExtents = geometry->getUnscaledMeshExtents();
                        results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
                        results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
                    }
                }

            }
        });
    }

    return convertLocationToScriptSemantics(results);
}
bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
                                            std::function<bool(EntitySimulation*, EntityItemPointer)> actor) {
    if (!_entityTree) {
        return false;
    }

    EntityItemPointer entity;
    bool doTransmit = false;
    _entityTree->withWriteLock([&] {
        EntitySimulation* simulation = _entityTree->getSimulation();
        entity = _entityTree->findEntityByEntityItemID(entityID);
        if (!entity) {
            qDebug() << "actionWorker -- unknown entity" << entityID;
            return;
        }

        if (!simulation) {
            qDebug() << "actionWorker -- no simulation" << entityID;
            return;
        }

        doTransmit = actor(simulation, entity);
        if (doTransmit) {
            _entityTree->entityChanged(entity);
        }
    });

    // transmit the change
    if (doTransmit) {
        EntityItemProperties properties;
        _entityTree->withReadLock([&] {
            properties = entity->getProperties();
        });

        properties.setActionDataDirty();
        auto now = usecTimestampNow();
        properties.setLastEdited(now);
        queueEntityMessage(PacketType::EntityEdit, entityID, properties);
    }

    return doTransmit;
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
                                                                                    Octree::lockType lockType,
                                                                                    bool precisionPicking) {


    RayToEntityIntersectionResult result;
    if (_entityTree) {
        OctreeElementPointer element;
        EntityItemPointer intersectedEntity = NULL;
        result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
                                                                result.surfaceNormal, (void**)&intersectedEntity, lockType, &result.accurate,
                                                                precisionPicking);
        if (result.intersects && intersectedEntity) {
            result.entityID = intersectedEntity->getEntityItemID();
            result.properties = intersectedEntity->getProperties();
            result.intersection = ray.origin + (ray.direction * result.distance);
        }
    }
    return result;
}
bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor) {
    if (!_entityTree) {
        return false;
    }

    EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
    if (!entity) {
        qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
    }

    EntityTypes::EntityType entityType = entity->getType();

    if (entityType != EntityTypes::Line) {
        return false;
    }

    auto now = usecTimestampNow();

    auto lineEntity = std::static_pointer_cast<LineEntityItem>(entity);
    bool success;
    _entityTree->withWriteLock([&] {
        success = actor(*lineEntity);
        entity->setLastEdited(now);
        entity->setLastBroadcast(now);
    });

    EntityItemProperties properties;
    _entityTree->withReadLock([&] {
        properties = entity->getProperties();
    });

    properties.setLinePointsDirty();
    properties.setLastEdited(now);

    queueEntityMessage(PacketType::EntityEdit, entityID, properties);
    return success;
}
Beispiel #7
0
void EntityServer::startDynamicDomainVerification() {
    qCDebug(entities) << "Starting Dynamic Domain Verification...";

    QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));

    EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
    QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());

    QHashIterator<QString, EntityItemID> i(localMap);
    qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
    while (i.hasNext()) {
        i.next();

        EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());

        if (entity) {
            if (!entity->getProperties().verifyStaticCertificateProperties()) {
                qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
                    << "static certificate verification.";
                // Delete the entity if it doesn't pass static certificate verification
                tree->deleteEntity(i.value(), true);
            } else {

                QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
                QNetworkRequest networkRequest;
                networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
                networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
                QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
                requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
                QJsonObject request;
                request["certificate_id"] = i.key();
                networkRequest.setUrl(requestURL);

                QNetworkReply* networkReply = NULL;
                networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());

                connect(networkReply, &QNetworkReply::finished, [=]() {
                    QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
                    jsonObject = jsonObject["data"].toObject();

                    if (networkReply->error() == QNetworkReply::NoError) {
                        if (jsonObject["domain_id"].toString() != thisDomainID) {
                            qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
                                << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
                            tree->deleteEntity(i.value(), true);
                        } else {
                            qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value();
                        }
                    } else {
                        qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << i.value()
                            << "More info:" << jsonObject;
                        tree->deleteEntity(i.value(), true);
                    }

                    networkReply->deleteLater();
                });
            }
        } else {
            qCWarning(entities) << "During DDV, an entity with ID" << i.value() << "was NOT found in the Entity Tree!";
        }
    }

    int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
    qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds";
    _dynamicDomainVerificationTimer.start(nextInterval);
}
bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut,
        bool& wasChanged, EntityTree::FilterType filterType, EntityItemID& itemID, EntityItemPointer& existingEntity) {
    
    // get the ids of all the zones (plus the global entity edit filter) that the position
    // lies within
    auto zoneIDs = getZonesByPosition(position);
    for (auto id : zoneIDs) {
        if (!itemID.isInvalidID() && id == itemID) {
            continue;
        }
        
        // get the filter pair, etc...  
        _lock.lockForRead();
        FilterData filterData = _filterDataMap.value(id);
        _lock.unlock();
    
        if (filterData.valid()) {
            if (filterData.rejectAll) {
                return false;
            }

            // check to see if this filter wants to filter this message type
            if ((!filterData.wantsToFilterEdit && filterType == EntityTree::FilterType::Edit) ||
                (!filterData.wantsToFilterPhysics && filterType == EntityTree::FilterType::Physics) ||
                (!filterData.wantsToFilterDelete && filterType == EntityTree::FilterType::Delete) ||
                (!filterData.wantsToFilterAdd && filterType == EntityTree::FilterType::Add)) {

                wasChanged = false;
                return true; // accept the message
            }

            auto oldProperties = propertiesIn.getDesiredProperties();
            auto specifiedProperties = propertiesIn.getChangedProperties();
            propertiesIn.setDesiredProperties(specifiedProperties);
            QScriptValue inputValues = propertiesIn.copyToScriptValue(filterData.engine, false, true, true);
            propertiesIn.setDesiredProperties(oldProperties);

            auto in = QJsonValue::fromVariant(inputValues.toVariant()); // grab json copy now, because the inputValues might be side effected by the filter.

            QScriptValueList args;
            args << inputValues;
            args << filterType;

            // get the current properties for then entity and include them for the filter call
            if (existingEntity && filterData.wantsOriginalProperties) {
                auto currentProperties = existingEntity->getProperties(filterData.includedOriginalProperties);
                QScriptValue currentValues = currentProperties.copyToScriptValue(filterData.engine, false, true, true);
                args << currentValues;
            }


            // get the zone properties
            if (filterData.wantsZoneProperties) {
                auto zoneEntity = _tree->findEntityByEntityItemID(id);
                if (zoneEntity) {
                    auto zoneProperties = zoneEntity->getProperties(filterData.includedZoneProperties);
                    QScriptValue zoneValues = zoneProperties.copyToScriptValue(filterData.engine, false, true, true);

                    if (filterData.wantsZoneBoundingBox) {
                        bool success = true;
                        AABox aaBox = zoneEntity->getAABox(success);
                        if (success) {
                            QScriptValue boundingBox = filterData.engine->newObject();
                            QScriptValue bottomRightNear = vec3ToScriptValue(filterData.engine, aaBox.getCorner());
                            QScriptValue topFarLeft = vec3ToScriptValue(filterData.engine, aaBox.calcTopFarLeft());
                            QScriptValue center = vec3ToScriptValue(filterData.engine, aaBox.calcCenter());
                            QScriptValue boundingBoxDimensions = vec3ToScriptValue(filterData.engine, aaBox.getDimensions());
                            boundingBox.setProperty("brn", bottomRightNear);
                            boundingBox.setProperty("tfl", topFarLeft);
                            boundingBox.setProperty("center", center);
                            boundingBox.setProperty("dimensions", boundingBoxDimensions);
                            zoneValues.setProperty("boundingBox", boundingBox);
                        }
                    }

                    // If this is an add or delete, or original properties weren't requested
                    // there won't be original properties in the args, but zone properties need
                    // to be the fourth parameter, so we need to pad the args accordingly
                    int EXPECTED_ARGS = 3;
                    if (args.length() < EXPECTED_ARGS) {
                        args << QScriptValue();
                    }
                    assert(args.length() == EXPECTED_ARGS); // we MUST have 3 args by now!
                    args << zoneValues;
                }
            }

            QScriptValue result = filterData.filterFn.call(_nullObjectForFilter, args);

            if (filterData.uncaughtExceptions()) {
                return false;
            }

            if (result.isObject()) {
                // make propertiesIn reflect the changes, for next filter...
                propertiesIn.copyFromScriptValue(result, false);

                // and update propertiesOut too.  TODO: this could be more efficient...
                propertiesOut.copyFromScriptValue(result, false);
                // Javascript objects are == only if they are the same object. To compare arbitrary values, we need to use JSON.
                auto out = QJsonValue::fromVariant(result.toVariant());
                wasChanged |= (in != out);
            } else if (result.isBool()) {

                // if the filter returned false, then it's authoritative
                if (!result.toBool()) {
                    return false;
                }

                // otherwise, assume it wants to pass all properties
                propertiesOut = propertiesIn;
                wasChanged = false;
                
            } else {
                return false;
            }
        }
    }
    // if we made it here, 
    return true;
}