401 lines · 18.6 KB · C
RawDownload
////  EntityTree.h//  libraries/entities/src////  Created by Brad Hefta-Gaub on 12/4/13.//  Copyright 2013 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// #ifndef hifi_EntityTree_h#define hifi_EntityTree_h #include <QSet>#include <QVector> #include <HelperScriptEngine.h>#include <Octree.h>#include <SpatialParentFinder.h> #include "AddEntityOperator.h"#include "EntityTreeElement.h"#include "DeleteEntityOperator.h"#include "MovingEntitiesOperator.h" class EntityTree;using EntityTreePointer = std::shared_ptr<EntityTree>; class EntitySimulation; namespace EntityQueryFilterSymbol {    static const QString NonDefault = "+";} class NewlyCreatedEntityHook {public:    virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) = 0;}; class SendEntitiesOperationArgs {public:    glm::vec3 root;    QString entityHostType;    EntityTree* ourTree;    EntityTreePointer otherTree;    QHash<EntityItemID, EntityItemID>* map;}; class EntityTree : public Octree, public SpatialParentTree {    Q_OBJECTpublic:    enum FilterType {        Add,        Edit,        Physics,        Delete    };    EntityTree(bool shouldReaverage = false);    virtual ~EntityTree();     void createRootElement();      void setEntityMaxTmpLifetime(float maxTmpEntityLifetime) { _maxTmpEntityLifetime = maxTmpEntityLifetime; }    void setEntityScriptSourceAllowlist(const QString& entityScriptSourceAllowlist);     /// Implements our type specific root element factory    virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL) override;     /// Type safe version of getRoot()    EntityTreeElementPointer getRoot() {        if (!_rootElement) {            createRootElement();        }        return std::static_pointer_cast<EntityTreeElement>(_rootElement);    }      virtual void eraseDomainAndNonOwnedEntities() override;    virtual void eraseAllOctreeElements(bool createNewRoot = true) override;     virtual void readBitstreamToTree(const unsigned char* bitstream,            uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args) override;    int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);     // These methods will allow the OctreeServer to send your tree inbound edit packets of your    // own definition. Implement these to allow your octree based server to support editing    virtual PacketType expectedDataPacketType() const override { return PacketType::EntityData; }    virtual bool handlesEditPacketType(PacketType packetType) const override;    void fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties);    virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,                                      const SharedNodePointer& senderNode) override;     virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction,        QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,        PickFilter searchFilter, OctreeElementPointer& element, float& distance,        BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,        Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);     virtual EntityItemID evalParabolaIntersection(const PickParabola& parabola,        QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,        PickFilter searchFilter, OctreeElementPointer& element, glm::vec3& intersection,        float& distance, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,        Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);     virtual bool rootElementHasData() const override { return true; }     virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override;     // Why preUpdate() and update()?    // Because sometimes we need to do stuff between the two.    void preUpdate() override;    void update(bool simulate = true) override;     // The newer API...    void postAddEntity(EntityItemPointer entityItem);     EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool isClone = false, const bool isImport = false);     // use this method if you only know the entityID    bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));     // check if the avatar is a child of this entity, If so set the avatar parentID to null    void unhookChildAvatar(const EntityItemID entityID);    void cleanupCloneIDs(const EntityItemID& entityID);    void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true);     void deleteEntitiesByID(const std::vector<EntityItemID>& entityIDs, bool force = false, bool ignoreWarnings = true);    void deleteEntitiesByPointer(const std::vector<EntityItemPointer>& entities);     EntityItemPointer findEntityByID(const QUuid& id) const;    EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const;    virtual SpatiallyNestablePointer findByID(const QUuid& id) const override { return findEntityByID(id); }     EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID     QUuid evalClosestEntity(const glm::vec3& position, float targetRadius, PickFilter searchFilter);    void evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector<QUuid>& foundEntities);    void evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector<QUuid>& foundEntities);    void evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector<QUuid>& foundEntities);    void evalEntitiesInSphereWithTags(const glm::vec3& center, float radius, const QVector<QString>& tags, bool caseSensitive, PickFilter searchFilter, QVector<QUuid>& foundEntities);    void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector<QUuid>& foundEntities);    void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector<QUuid>& foundEntities);    void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector<QUuid>& foundEntities);     void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);    void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);     bool hasAnyDeletedEntities() const {         QReadLocker locker(&_recentlyDeletedEntitiesLock);        return _recentlyDeletedEntityItemIDs.size() > 0;    }     bool hasEntitiesDeletedSince(quint64 sinceTime);    static quint64 getAdjustedConsiderSince(quint64 sinceTime);     QMultiMap<quint64, QUuid> getRecentlyDeletedEntityIDs() const {         QReadLocker locker(&_recentlyDeletedEntitiesLock);        return _recentlyDeletedEntityItemIDs;    }     void forgetEntitiesDeletedBefore(quint64 sinceTime);     int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);    int processEraseMessageDetails(const QByteArray& buffer, const SharedNodePointer& sourceNode);    bool shouldEraseEntity(EntityItemID entityID, const SharedNodePointer& sourceNode);      EntityTreeElementPointer getContainingElement(const EntityItemID& entityItemID)  /*const*/;    void addEntityMapEntry(EntityItemPointer entity);    void clearEntityMapEntry(const EntityItemID& id);    void debugDumpMap();    virtual void dumpTree() override;    virtual void pruneTree() override;     static QByteArray remapActionDataIDs(QByteArray actionData, QHash<EntityItemID, EntityItemID>& map);     QVector<EntityItemID> sendEntities(EntityEditPacketSender* packetSender, EntityTreePointer localTree,                                       const QString& entityHostType, float x, float y, float z);     void entityChanged(EntityItemPointer entity);     void emitEntityScriptChanging(const EntityItemID& entityItemID, const QString& oldScriptURL, const QString& newScriptURL);    void emitEntityServerScriptChanging(const EntityItemID& entityItemID, const QString& oldScriptURL, const QString& newScriptURL);     void setSimulation(EntitySimulationPointer simulation);    EntitySimulationPointer getSimulation() const { return _simulation; }     bool wantEditLogging() const { return _wantEditLogging; }    void setWantEditLogging(bool value) { _wantEditLogging = value; }     bool wantTerseEditLogging() const { return _wantTerseEditLogging; }    void setWantTerseEditLogging(bool value) { _wantTerseEditLogging = value; }     virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues,                            bool skipThoseWithBadParents) override;    virtual bool readFromMap(QVariantMap& entityDescription, const bool isImport = false) override;    virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) override;      glm::vec3 getContentsDimensions();    float getContentsLargestDimension();     virtual void resetEditStats() override {        _totalEditMessages = 0;        _totalUpdates = 0;        _totalCreates = 0;        _totalDecodeTime = 0;        _totalLookupTime = 0;        _totalUpdateTime = 0;        _totalCreateTime = 0;        _totalLoggingTime = 0;    }     virtual quint64 getAverageDecodeTime() const override { return _totalEditMessages == 0 ? 0 : _totalDecodeTime / _totalEditMessages; }    virtual quint64 getAverageLookupTime() const override { return _totalEditMessages == 0 ? 0 : _totalLookupTime / _totalEditMessages; }    virtual quint64 getAverageUpdateTime() const override { return _totalUpdates == 0 ? 0 : _totalUpdateTime / _totalUpdates; }    virtual quint64 getAverageCreateTime() const override { return _totalCreates == 0 ? 0 : _totalCreateTime / _totalCreates; }    virtual quint64 getAverageLoggingTime() const override { return _totalEditMessages == 0 ? 0 : _totalLoggingTime / _totalEditMessages; }    virtual quint64 getAverageFilterTime() const override { return _totalEditMessages == 0 ? 0 : _totalFilterTime / _totalEditMessages; }     void trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead);    quint64 getAverageEditDeltas() const        { return _totalTrackedEdits == 0 ? 0 : _totalEditDeltas / _totalTrackedEdits; }    quint64 getAverageEditBytes() const        { return _totalTrackedEdits == 0 ? 0 : _totalEditBytes / _totalTrackedEdits; }    quint64 getMaxEditDelta() const { return _maxEditDelta; }    quint64 getTotalTrackedEdits() const { return _totalTrackedEdits; }     EntityTreePointer getThisPointer() { return std::static_pointer_cast<EntityTree>(shared_from_this()); }     bool isDeletedEntity(const QUuid& id) {        QReadLocker locker(&_deletedEntitiesLock);        return _deletedEntityItemIDs.contains(id);    }     // these are used to call through to EntityItems    Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name) const;    Q_INVOKABLE QStringList getJointNames(const QUuid& entityID) const;     void knowAvatarID(const QUuid& avatarID);    void forgetAvatarID(const QUuid& avatarID);    void deleteDescendantsOfAvatar(const QUuid& avatarID);    void removeFromChildrenOfAvatars(EntityItemPointer entity);     void addToNeedsParentFixupList(EntityItemPointer entity);     void notifyNewCollisionSoundURL(const QString& newCollisionSoundURL, const EntityItemID& entityID);     static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME;     QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); }    void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }     void swapStaleProxies(std::vector<int>& proxies) { proxies.swap(_staleProxies); }     void setIsServerlessMode(bool value) { _serverlessDomain = value; }    bool isServerlessMode() const { return _serverlessDomain; }     void setIsEntityServer(bool value) { _entityServer = value; }    bool isEntityServer() const { return _entityServer; }     static void setGetEntityObjectOperator(std::function<QObject*(const QUuid&)> getEntityObjectOperator) { _getEntityObjectOperator = getEntityObjectOperator; }    static QObject* getEntityObject(const QUuid& id);     static void setTextSizeOperator(std::function<QSizeF(const QUuid&, const QString&)> textSizeOperator) { _textSizeOperator = textSizeOperator; }    static QSizeF textSize(const QUuid& id, const QString& text);     static void setEntityClicksCapturedOperator(std::function<bool()> areEntityClicksCapturedOperator) { _areEntityClicksCapturedOperator = areEntityClicksCapturedOperator; }    static bool areEntityClicksCaptured();     static void setEmitScriptEventOperator(std::function<void(const QUuid&, const QVariant&)> emitScriptEventOperator) { _emitScriptEventOperator = emitScriptEventOperator; }    static void emitScriptEvent(const QUuid& id, const QVariant& message);     static void setGetUnscaledDimensionsForIDOperator(std::function<glm::vec3(const QUuid&)> getUnscaledDimensionsForIDOperator) { _getUnscaledDimensionsForIDOperator = getUnscaledDimensionsForIDOperator; }    static glm::vec3 getUnscaledDimensionsForID(const QUuid& id);     std::map<QString, QString> getNamedPaths() const { return _namedPaths; }     void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,                                 bool force, bool tellServer); signals:    void deletingEntity(const EntityItemID& entityID);    void deletingEntityPointer(EntityItem* entityID);    void addingEntity(const EntityItemID& entityID);    void addingEntityPointer(EntityItem* entityID);    void editingEntityPointer(const EntityItemPointer& entityID);    void entityScriptChanging(const EntityItemID& entityItemID, const QString& oldScriptURL, const QString& newScriptURL);    void entityServerScriptChanging(const EntityItemID& entityItemID, const QString& oldScriptURL, const QString& newScriptURL);    void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);    void clearingEntities();    void killChallengeOwnershipTimeoutTimer(const EntityItemID& certID); protected:     void recursivelyFilterAndCollectForDelete(const EntityItemPointer& entity, std::vector<EntityItemPointer>& entitiesToDelete, bool force) const;    void processRemovedEntities(const DeleteEntityOperator& theOperator);    bool updateEntity(EntityItemPointer entity, const EntityItemProperties& properties,            const SharedNodePointer& senderNode = SharedNodePointer(nullptr));    static bool sendEntitiesOperation(const OctreeElementPointer& element, void* extraData);    static void bumpTimestamp(EntityItemProperties& properties);     void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);     bool isScriptInAllowlist(const QString& scriptURL);     QReadWriteLock _newlyCreatedHooksLock;    QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;     mutable QReadWriteLock _recentlyDeletedEntitiesLock; /// lock of server side recent deletes    QMultiMap<quint64, QUuid> _recentlyDeletedEntityItemIDs; /// server side recent deletes     mutable QReadWriteLock _deletedEntitiesLock; /// lock of client side recent deletes    QSet<QUuid> _deletedEntityItemIDs; /// client side recent deletes     void clearDeletedEntities() {        QWriteLocker locker(&_deletedEntitiesLock);        _deletedEntityItemIDs.clear();    }     void trackDeletedEntity(const QUuid& id) {        QWriteLocker locker(&_deletedEntitiesLock);        _deletedEntityItemIDs << id;    }     mutable QReadWriteLock _entityMapLock;    QHash<EntityItemID, EntityItemPointer> _entityMap;     EntitySimulationPointer _simulation;     bool _wantEditLogging = false;    bool _wantTerseEditLogging = false;      // some performance tracking properties - only used in server trees    int _totalEditMessages = 0;    int _totalUpdates = 0;    int _totalCreates = 0;    mutable quint64 _totalDecodeTime = 0;    mutable quint64 _totalLookupTime = 0;    mutable quint64 _totalUpdateTime = 0;    mutable quint64 _totalCreateTime = 0;    mutable quint64 _totalLoggingTime = 0;    mutable quint64 _totalFilterTime = 0;     // these performance statistics are only used in the client    void resetClientEditStats();    int _totalTrackedEdits = 0;    quint64 _totalEditBytes = 0;    quint64 _totalEditDeltas = 0;    quint64 _maxEditDelta = 0;    quint64 _treeResetTime = 0;     void fixupNeedsParentFixups(); // try to hook members of _needsParentFixup to parent instances    QVector<EntityItemWeakPointer> _needsParentFixup; // entites with a parentID but no (yet) known parent instance    mutable QReadWriteLock _needsParentFixupLock;     std::mutex _avatarIDsLock;    // we maintain a list of avatarIDs to notice when an entity is a child of one.    QSet<QUuid> _avatarIDs; // IDs of avatars connected to entity server    std::mutex _childrenOfAvatarsLock;    QHash<QUuid, QSet<EntityItemID>> _childrenOfAvatars;  // which entities are children of which avatars     float _maxTmpEntityLifetime { DEFAULT_MAX_TMP_ENTITY_LIFETIME };     bool filterProperties(const EntityItemPointer& existingEntity, EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged, FilterType filterType) const;    bool _hasEntityEditFilter{ false };    QStringList _entityScriptSourceAllowlist;     MovingEntitiesOperator _entityMover;    QHash<EntityItemID, EntityItemPointer> _entitiesToAdd; private:    std::shared_ptr<AvatarData> _myAvatar{ nullptr };     static std::function<QObject*(const QUuid&)> _getEntityObjectOperator;    static std::function<QSizeF(const QUuid&, const QString&)> _textSizeOperator;    static std::function<bool()> _areEntityClicksCapturedOperator;    static std::function<void(const QUuid&, const QVariant&)> _emitScriptEventOperator;    static std::function<glm::vec3(const QUuid&)> _getUnscaledDimensionsForIDOperator;     std::vector<int32_t> _staleProxies;     bool _serverlessDomain { false };    bool _entityServer { false };     std::map<QString, QString> _namedPaths;     // Return an AACube containing object and all its entity descendants    AACube updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,                                         MovingEntitiesOperator& moveOperator, bool force, bool tellServer);     // Script engine for writing entity tree data to and from JSON    HelperScriptEngine _helperScriptEngine;}; void convertGrabUserDataToProperties(EntityItemProperties& properties); #endif // hifi_EntityTree_h