2936 lines · 109.7 KB · Text
RawDownload
////  EntityItem.cpp//  libraries/models/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// #include "EntityItem.h" #include <QtCore/QObject>#include <QtEndian>#include <QJsonDocument>#include <NetworkingConstants.h>#include <MetaverseAPI.h>#include <NetworkAccessManager.h>#include <QtNetwork/QNetworkReply>#include <QtNetwork/QNetworkRequest> #include <glm/gtx/transform.hpp>#include <glm/gtx/component_wise.hpp> #include <BufferParser.h>#include <ByteCountCoding.h>#include <GLMHelpers.h>#include <Octree.h>#include <PhysicsHelpers.h>#include <Profile.h>#include <RegisteredMetaTypes.h>#include <SharedUtil.h> // usecTimestampNow()#include <LogHandler.h>#include <Extents.h>#include <QVariantGLM.h>#include <Grab.h> #include "EntityScriptingInterface.h"#include "EntitiesLogging.h"#include "EntityTree.h"#include "EntitySimulation.h"#include "EntityDynamicFactoryInterface.h" //#define WANT_DEBUG Q_DECLARE_METATYPE(EntityItemPointer);int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>(); int EntityItem::_maxActionsDataSize = 800;quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;std::function<void(const EntityItemID&, const QString&, const QString&)> EntityItem::_updateScriptUserDataOperator = nullptr; EntityItem::EntityItem(const EntityItemID& entityItemID) :    SpatiallyNestable(NestableType::Entity, entityItemID){    setLocalVelocity(ENTITY_ITEM_DEFAULT_VELOCITY);    setLocalAngularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY);    setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS);    // explicitly set transform parts to set dirty flags used by batch rendering    locationChanged();    dimensionsChanged();    quint64 now = usecTimestampNow();    _lastSimulated = now;    _lastUpdated = now;} EntityItem::~EntityItem() {    // these pointers MUST be correct at delete, else we probably have a dangling backpointer    // to this EntityItem in the corresponding data structure.    assert(!_simulated || (!_element && !_physicsInfo));    assert(!_element);    assert(!_physicsInfo);} EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const {    EntityPropertyFlags requestedProperties; @Base_REQUESTED_PROPS@     return requestedProperties;} OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,                                            EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,                                            const bool destinationNodeCanGetAndSetPrivateUserData) const {     // ALL this fits...    //    object ID [16 bytes]    //    ByteCountCoded(type code) [~1 byte]    //    last edited [8 bytes]    //    ByteCountCoded(last_edited to last_updated delta) [~1-8 bytes]    //    PropertyFlags<>( everything ) [1-2 bytes]    // ~27-35 bytes...     OctreeElement::AppendState appendState = OctreeElement::COMPLETED; // assume the best     // encode our ID as a byte count coded byte stream    QByteArray encodedID = getID().toRfc4122();     // encode our type as a byte count coded byte stream    ByteCountCoded<quint32> typeCoder = getType();    QByteArray encodedType = typeCoder;     // last updated (animations, non-physics changes)    quint64 updateDelta = getLastUpdated() <= getLastEdited() ? 0 : getLastUpdated() - getLastEdited();    ByteCountCoded<quint64> updateDeltaCoder = updateDelta;    QByteArray encodedUpdateDelta = updateDeltaCoder;     // last simulated (velocity, angular velocity, physics changes)    quint64 simulatedDelta = getLastSimulated() <= getLastEdited() ? 0 : getLastSimulated() - getLastEdited();    ByteCountCoded<quint64> simulatedDeltaCoder = simulatedDelta;    QByteArray encodedSimulatedDelta = simulatedDeltaCoder;      EntityPropertyFlags propertyFlags(PROP_LAST_ITEM);    EntityPropertyFlags requestedProperties = getEntityProperties(params);     // these properties are not sent over the wire    requestedProperties -= PROP_ENTITY_HOST_TYPE;    requestedProperties -= PROP_OWNING_AVATAR_ID;    requestedProperties -= PROP_VISIBLE_IN_SECONDARY_CAMERA;     // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item,    // then our entityTreeElementExtraEncodeData should include data about which properties we need to append.    if (entityTreeElementExtraEncodeData && entityTreeElementExtraEncodeData->entities.contains(getEntityItemID())) {        requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID());    }     QString privateUserData = "";    if (destinationNodeCanGetAndSetPrivateUserData) {        privateUserData = getPrivateUserData();    }     EntityPropertyFlags propertiesDidntFit = requestedProperties;     LevelDetails entityLevel = packetData->startLevel();     quint64 lastEdited = getLastEdited();     #ifdef WANT_DEBUG        float editedAgo = getEditedAgo();        QString agoAsString = formatSecondsElapsed(editedAgo);        qCDebug(entities) << "Writing entity " << getEntityItemID() << " to buffer, lastEdited =" << lastEdited                        << " ago=" << editedAgo << "seconds - " << agoAsString;    #endif     bool successIDFits = false;    bool successTypeFits = false;    bool successCreatedFits = false;    bool successLastEditedFits = false;    bool successLastUpdatedFits = false;    bool successLastSimulatedFits = false;    bool successPropertyFlagsFits = false;    int propertyFlagsOffset = 0;    int oldPropertyFlagsLength = 0;    QByteArray encodedPropertyFlags;    int propertyCount = 0;     successIDFits = packetData->appendRawData(encodedID);    if (successIDFits) {        successTypeFits = packetData->appendRawData(encodedType);    }    if (successTypeFits) {        successCreatedFits = packetData->appendValue(_created);    }    if (successCreatedFits) {        successLastEditedFits = packetData->appendValue(lastEdited);    }    if (successLastEditedFits) {        successLastUpdatedFits = packetData->appendRawData(encodedUpdateDelta);    }    if (successLastUpdatedFits) {        successLastSimulatedFits = packetData->appendRawData(encodedSimulatedDelta);    }     if (successLastSimulatedFits) {        propertyFlagsOffset = packetData->getUncompressedByteOffset();        encodedPropertyFlags = propertyFlags;        oldPropertyFlagsLength = encodedPropertyFlags.length();        successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags);    }     bool headerFits = successIDFits && successTypeFits && successCreatedFits && successLastEditedFits                              && successLastUpdatedFits && successPropertyFlagsFits;     int startOfEntityItemData = packetData->getUncompressedByteOffset();     if (headerFits) {        bool successPropertyFits;         propertyFlags -= PROP_LAST_ITEM; // clear the last item for now, we may or may not set it as the actual item         // NOTE: When we enable partial packing of entity properties, we'll want to pack simulationOwner, transform, and velocity properties near each other        // since they will commonly be transmitted together.  simulationOwner must always go first, to avoid race conditions of simulation ownership bids        // These items would go here once supported....        //      PROP_PAGED_PROPERTY,        //      PROP_CUSTOM_PROPERTIES_INCLUDED,         // convert AVATAR_SELF_ID to actual sessionUUID.        QUuid actualParentID = getParentID();        auto nodeList = DependencyManager::get<NodeList>();        if (actualParentID == AVATAR_SELF_ID) {            actualParentID = nodeList->getSessionUUID();        } @Base_ENTITY_APPEND@         appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,                                requestedProperties,                                propertyFlags,                                propertiesDidntFit,                                propertyCount,                                appendState);    }     if (propertyCount > 0) {        int endOfEntityItemData = packetData->getUncompressedByteOffset();        encodedPropertyFlags = propertyFlags;        int newPropertyFlagsLength = encodedPropertyFlags.length();        packetData->updatePriorBytes(propertyFlagsOffset,                (const unsigned char*)encodedPropertyFlags.constData(), encodedPropertyFlags.length());         // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet.        if (newPropertyFlagsLength < oldPropertyFlagsLength) {            int oldSize = packetData->getUncompressedSize();            const unsigned char* modelItemData = packetData->getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength);            int modelItemDataLength = endOfEntityItemData - startOfEntityItemData;            int newEntityItemDataStart = propertyFlagsOffset + newPropertyFlagsLength;            packetData->updatePriorBytes(newEntityItemDataStart, modelItemData, modelItemDataLength);            int newSize = oldSize - (oldPropertyFlagsLength - newPropertyFlagsLength);            packetData->setUncompressedSize(newSize);         } else {            assert(newPropertyFlagsLength == oldPropertyFlagsLength); // should not have grown        }         packetData->endLevel(entityLevel);    } else {        packetData->discardLevel(entityLevel);        appendState = OctreeElement::NONE; // if we got here, then we didn't include the item    }     // If any part of the model items didn't fit, then the element is considered partial    if (appendState != OctreeElement::COMPLETED) {        // add this item into our list for the next appendElementData() pass        entityTreeElementExtraEncodeData->entities.insert(getEntityItemID(), propertiesDidntFit);    }     // if any part of our entity was sent, call trackSend    if (appendState != OctreeElement::NONE) {        params.trackSend(getID(), getLastEdited());    }     return appendState;} // TODO: My goal is to get rid of this concept completely. The old code (and some of the current code) used this// result to calculate if a packet being sent to it was potentially bad or corrupt. I've adjusted this to now// only consider the minimum header bytes as being required. But it would be preferable to completely eliminate// this logic from the callers.int EntityItem::expectedBytes() {    // Header bytes    //    object ID [16 bytes]    //    ByteCountCoded(type code) [~1 byte]    //    last edited [8 bytes]    //    ByteCountCoded(last_edited to last_updated delta) [~1-8 bytes]    //    PropertyFlags<>( everything ) [1-2 bytes]    // ~27-35 bytes...    const int MINIMUM_HEADER_BYTES = 27;    return MINIMUM_HEADER_BYTES;} const uint8_t PENDING_STATE_NOTHING = 0;const uint8_t PENDING_STATE_TAKE = 1;const uint8_t PENDING_STATE_RELEASE = 2; // clients use this method to unpack FULL updates from entity-serverint EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {    setSourceUUID(args.sourceUUID);     args.entitiesPerPacket++;     // Header bytes    //    object ID [16 bytes]    //    ByteCountCoded(type code) [~1 byte]    //    last edited [8 bytes]    //    ByteCountCoded(last_edited to last_updated delta) [~1-8 bytes]    //    PropertyFlags<>( everything ) [1-2 bytes]    // ~27-35 bytes...    const int MINIMUM_HEADER_BYTES = 27;     if (bytesLeftToRead < MINIMUM_HEADER_BYTES) {        return 0;    }     int64_t clockSkew = 0;    uint64_t maxPingRoundTrip = 33333; // two frames periods at 60 fps    if (args.sourceNode) {        clockSkew = args.sourceNode->getClockSkewUsec();        const float MSECS_PER_USEC = 1000;        maxPingRoundTrip += args.sourceNode->getPingMs() * MSECS_PER_USEC;    }     BufferParser parser(data, bytesLeftToRead); #ifdef DEBUG#define VALIDATE_ENTITY_ITEM_PARSER 1#endif #ifdef VALIDATE_ENTITY_ITEM_PARSER    int bytesRead = 0;    int originalLength = bytesLeftToRead;    // TODO: figure out a way to avoid the big deep copy below.    QByteArray originalDataBuffer((const char*)data, originalLength); // big deep copy!    const unsigned char* dataAt = data;#endif     // id    parser.readUuid(_id);#ifdef VALIDATE_ENTITY_ITEM_PARSER    {        QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size        QUuid id = QUuid::fromRfc4122(encodedID);        dataAt += encodedID.size();        bytesRead += encodedID.size();        Q_ASSERT(id == _id);        Q_ASSERT(parser.offset() == (unsigned int) bytesRead);    }#endif     // type    parser.readCompressedCount<quint32>((quint32&)_type);#ifdef VALIDATE_ENTITY_ITEM_PARSER    QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size    ByteCountCoded<quint32> typeCoder = encodedType;    encodedType = typeCoder; // determine true length    dataAt += encodedType.size();    bytesRead += encodedType.size();    quint32 type = typeCoder;    EntityTypes::EntityType oldType = (EntityTypes::EntityType)type;    Q_ASSERT(oldType == _type);    Q_ASSERT(parser.offset() == (unsigned int) bytesRead);#endif     bool overwriteLocalData = true; // assume the new content overwrites our local data    quint64 now = usecTimestampNow();    bool somethingChanged = false;     // _created    {        quint64 createdFromBuffer = 0;        parser.readValue(createdFromBuffer);#ifdef VALIDATE_ENTITY_ITEM_PARSER        {            quint64 createdFromBuffer2 = 0;            memcpy(&createdFromBuffer2, dataAt, sizeof(createdFromBuffer2));            dataAt += sizeof(createdFromBuffer2);            bytesRead += sizeof(createdFromBuffer2);            Q_ASSERT(createdFromBuffer2 == createdFromBuffer);            Q_ASSERT(parser.offset() == (unsigned int) bytesRead);        }#endif        if (_created == UNKNOWN_CREATED_TIME) {            // we don't yet have a _created timestamp, so we accept this one            createdFromBuffer -= clockSkew;            if (createdFromBuffer > now || createdFromBuffer == UNKNOWN_CREATED_TIME) {                createdFromBuffer = now;            }            _created = createdFromBuffer;        }    }     #ifdef WANT_DEBUG    {        quint64 lastEdited = getLastEdited();        float editedAgo = getEditedAgo();        QString agoAsString = formatSecondsElapsed(editedAgo);        QString ageAsString = formatSecondsElapsed(getAge());        qCDebug(entities) << "------------------------------------------";        qCDebug(entities) << "Loading entity " << getEntityItemID() << " from buffer...";        qCDebug(entities) << "------------------------------------------";        debugDump();        qCDebug(entities) << "------------------------------------------";        qCDebug(entities) << "    _created =" << _created;        qCDebug(entities) << "    age=" << getAge() << "seconds - " << ageAsString;        qCDebug(entities) << "    lastEdited =" << lastEdited;        qCDebug(entities) << "    ago=" << editedAgo << "seconds - " << agoAsString;    }    #endif     quint64 lastEditedFromBuffer = 0;     // TODO: we could make this encoded as a delta from _created    // _lastEdited    parser.readValue(lastEditedFromBuffer);#ifdef VALIDATE_ENTITY_ITEM_PARSER    {        quint64 lastEditedFromBuffer2 = 0;        memcpy(&lastEditedFromBuffer2, dataAt, sizeof(lastEditedFromBuffer2));        dataAt += sizeof(lastEditedFromBuffer2);        bytesRead += sizeof(lastEditedFromBuffer2);        Q_ASSERT(lastEditedFromBuffer2 == lastEditedFromBuffer);        Q_ASSERT(parser.offset() == (unsigned int) bytesRead);    }#endif    quint64 lastEditedFromBufferAdjusted = lastEditedFromBuffer == 0 ? 0 : lastEditedFromBuffer - clockSkew;    if (lastEditedFromBufferAdjusted > now) {        lastEditedFromBufferAdjusted = now;    }     bool fromSameServerEdit = (lastEditedFromBuffer == _lastEditedFromRemoteInRemoteTime);     #ifdef WANT_DEBUG        qCDebug(entities) << "data from server **************** ";        qCDebug(entities) << "                           entityItemID:" << getEntityItemID();        qCDebug(entities) << "                                    now:" << now;        qCDebug(entities) << "                          getLastEdited:" << debugTime(getLastEdited(), now);        qCDebug(entities) << "                   lastEditedFromBuffer:" << debugTime(lastEditedFromBuffer, now);        qCDebug(entities) << "                              clockSkew:" << clockSkew;        qCDebug(entities) << "           lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);        qCDebug(entities) << "                  _lastEditedFromRemote:" << debugTime(_lastEditedFromRemote, now);        qCDebug(entities) << "      _lastEditedFromRemoteInRemoteTime:" << debugTime(_lastEditedFromRemoteInRemoteTime, now);        qCDebug(entities) << "                     fromSameServerEdit:" << fromSameServerEdit;    #endif     bool ignoreServerPacket = false; // assume we'll use this server packet     // If this packet is from the same server edit as the last packet we accepted from the server    // we probably want to use it.    if (fromSameServerEdit) {        // If this is from the same sever packet, then check against any local changes since we got        // the most recent packet from this server time        if (_lastEdited > _lastEditedFromRemote) {            ignoreServerPacket = true;        }    } else {        // If this isn't from the same sever packet, then honor our skew adjusted times...        // If we've changed our local tree more recently than the new data from this packet        // then we will not be changing our values, instead we just read and skip the data        if (_lastEdited > lastEditedFromBufferAdjusted) {            ignoreServerPacket = true;        }    }     // before proceeding, check to see if this is an entity that we know has been deleted, which    // might happen in the case of out-of-order and/or recorvered packets, if we've deleted the entity    // we can confidently ignore this packet    EntityTreePointer tree = getTree();    if (tree && tree->isDeletedEntity(_id)) {        #ifdef WANT_DEBUG            qCDebug(entities) << "Received packet for previously deleted entity [" << _id << "] ignoring. "                "(inside " << __FUNCTION__ << ")";        #endif        ignoreServerPacket = true;    }     if (ignoreServerPacket) {        overwriteLocalData = false;        #ifdef WANT_DEBUG            qCDebug(entities) << "IGNORING old data from server!!! ****************";            debugDump();        #endif    } else {        #ifdef WANT_DEBUG            qCDebug(entities) << "USING NEW data from server!!! ****************";            debugDump();        #endif         // don't allow _lastEdited to be in the future        _lastEdited = lastEditedFromBufferAdjusted;        _lastEditedFromRemote = now;        _lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer;    }     // last updated is stored as ByteCountCoded delta from lastEdited    quint64 updateDelta;    parser.readCompressedCount(updateDelta);#ifdef VALIDATE_ENTITY_ITEM_PARSER    {        QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size        ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta;        quint64 updateDelta2 = updateDeltaCoder;        Q_ASSERT(updateDelta == updateDelta2);        encodedUpdateDelta = updateDeltaCoder; // determine true length        dataAt += encodedUpdateDelta.size();        bytesRead += encodedUpdateDelta.size();        Q_ASSERT(parser.offset() == (unsigned int) bytesRead);    }#endif     if (overwriteLocalData) {        _lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that        #ifdef WANT_DEBUG            qCDebug(entities) << "                           _lastUpdated:" << debugTime(_lastUpdated, now);            qCDebug(entities) << "                            _lastEdited:" << debugTime(_lastEdited, now);            qCDebug(entities) << "           lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);        #endif    } 

Lines 1–500 of 2936.