From 0c7afc39ca28984425194335a2f34f61e3873ab1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 17 Jul 2014 10:24:32 -0700 Subject: [PATCH] refactoring EntityItemID management and id assignment to be part of EntityItemID class instead of EntityItem class --- assignment-client/src/Agent.cpp | 2 +- .../src/entities/EntityServer.cpp | 5 + interface/src/DatagramProcessor.cpp | 3 +- libraries/entities/src/EntityItem.cpp | 43 +-- libraries/entities/src/EntityItem.h | 5 +- libraries/entities/src/EntityItemID.cpp | 112 ++++++++ libraries/entities/src/EntityItemID.h | 37 ++- .../entities/src/EntityScriptingInterface.cpp | 13 +- libraries/entities/src/EntityTree.cpp | 269 +++++++++++++++--- libraries/entities/src/EntityTree.h | 2 + libraries/entities/src/EntityTreeElement.cpp | 30 ++ libraries/entities/src/EntityTreeElement.h | 3 +- libraries/entities/src/EntityTypes.cpp | 3 + 13 files changed, 420 insertions(+), 107 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 977ba8f244..833ae32116 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -92,7 +92,7 @@ void Agent::readPendingDatagrams() { } else if (datagramPacketType == PacketTypeEntityAddResponse) { // this will keep creatorTokenIDs to IDs mapped correctly - EntityItem::handleAddEntityResponse(receivedPacket); + EntityItemID::handleAddEntityResponse(receivedPacket); // also give our local particle tree a chance to remap any internal locally created particles _entityViewer.getTree()->handleAddEntityResponse(receivedPacket); diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 18f1d8df60..4813abb8ba 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -47,6 +47,9 @@ void EntityServer::beforeRun() { } void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) { + +qDebug() << "EntityServer::entityCreated() newEntity.getEntityItemID()=" << newEntity.getEntityItemID(); + unsigned char outputBuffer[MAX_PACKET_SIZE]; unsigned char* copyAt = outputBuffer; @@ -66,6 +69,8 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo copyAt += sizeof(entityID); packetLength += sizeof(entityID); +qDebug() << "EntityServer::entityCreated() writeDatagram()"; + NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, senderNode); } diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 6d31fd0d64..d7a52e4ab6 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -61,8 +61,9 @@ void DatagramProcessor::processDatagrams() { application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket); break; case PacketTypeEntityAddResponse: +qDebug() << "DatagramProcessor::processDatagrams() PacketTypeEntityAddResponse..."; // this will keep creatorTokenIDs to IDs mapped correctly - EntityItem::handleAddEntityResponse(incomingPacket); + EntityItemID::handleAddEntityResponse(incomingPacket); application->getEntities()->getTree()->handleAddEntityResponse(incomingPacket); break; case PacketTypeParticleData: diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 28368328f2..2088c48b2d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -29,42 +29,6 @@ #include "EntityItem.h" #include "EntityTree.h" -uint32_t EntityItem::_nextID = 0; - -// for locally created models -std::map EntityItem::_tokenIDsToIDs; -uint32_t EntityItem::_nextCreatorTokenID = 0; - -uint32_t EntityItem::getIDfromCreatorTokenID(uint32_t creatorTokenID) { - if (_tokenIDsToIDs.find(creatorTokenID) != _tokenIDsToIDs.end()) { - return _tokenIDsToIDs[creatorTokenID]; - } - return UNKNOWN_ENTITY_ID; -} - -uint32_t EntityItem::getNextCreatorTokenID() { - uint32_t creatorTokenID = _nextCreatorTokenID; - _nextCreatorTokenID++; - return creatorTokenID; -} - -void EntityItem::handleAddEntityResponse(const QByteArray& packet) { - const unsigned char* dataAt = reinterpret_cast(packet.data()); - int numBytesPacketHeader = numBytesForPacketHeader(packet); - dataAt += numBytesPacketHeader; - - uint32_t creatorTokenID; - memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID)); - dataAt += sizeof(creatorTokenID); - - uint32_t entityItemID; - memcpy(&entityItemID, dataAt, sizeof(entityItemID)); - dataAt += sizeof(entityItemID); - - // add our token to id mapping - _tokenIDsToIDs[creatorTokenID] = entityItemID; -} - EntityItem::EntityItem() { _type = EntityTypes::Base; rgbColor noColor = { 0, 0, 0 }; @@ -118,12 +82,19 @@ EntityItem::~EntityItem() { } void EntityItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t id) { + + // TODO: is this what we want??? + /* if (id == NEW_ENTITY) { _id = _nextID; _nextID++; } else { _id = id; } + */ + + _id = id; + quint64 now = usecTimestampNow(); _lastEdited = now; _lastUpdated = now; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 7b4cbeb51c..fc18a92954 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -50,11 +50,12 @@ public: bool isKnownID() const { return getID() != UNKNOWN_ENTITY_ID; } EntityItemID getEntityItemID() const { return EntityItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_ENTITY_ID); } +/* // these methods allow you to create models, and later edit them. static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID); static uint32_t getNextCreatorTokenID(); static void handleAddEntityResponse(const QByteArray& packet); - +*/ // methods for getting/setting all properties of an entity EntityItemProperties getProperties() const; void setProperties(const EntityItemProperties& properties, bool forceCopy = false); @@ -150,9 +151,11 @@ protected: void initFromEntityItemID(const EntityItemID& entityItemID); virtual void init(glm::vec3 position, float radius, rgbColor color, uint32_t id = NEW_ENTITY); + /* static quint32 _nextID; static uint32_t _nextCreatorTokenID; /// used by the static interfaces for creator token ids static std::map _tokenIDsToIDs; + */ quint32 _id; EntityTypes::EntityType_t _type; diff --git a/libraries/entities/src/EntityItemID.cpp b/libraries/entities/src/EntityItemID.cpp index 910b69edd5..bc1d8028bf 100644 --- a/libraries/entities/src/EntityItemID.cpp +++ b/libraries/entities/src/EntityItemID.cpp @@ -12,8 +12,120 @@ #include #include +#include + #include "EntityItemID.h" +uint32_t EntityItemID::_nextID = 0; // TODO: should be changed to UUID + +// for locally created models +std::map EntityItemID::_tokenIDsToIDs; +uint32_t EntityItemID::_nextCreatorTokenID = 0; + + +EntityItemID::EntityItemID() : + id(NEW_ENTITY), + creatorTokenID(UNKNOWN_ENTITY_TOKEN), + isKnownID(false) +{ + //qDebug() << "EntityItemID::EntityItemID()... isKnownID=" + // << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; +}; + +EntityItemID::EntityItemID(const EntityItemID& other) : + id(other.id), + creatorTokenID(other.creatorTokenID), + isKnownID(other.isKnownID) +{ + //qDebug() << "EntityItemID::EntityItemID(const EntityItemID& other)... isKnownID=" + // << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; +} + + +EntityItemID::EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) : + id(id), + creatorTokenID(creatorTokenID), + isKnownID(isKnownID) +{ + //qDebug() << "EntityItemID::EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID)... isKnownID=" + // << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; +}; + +EntityItemID::EntityItemID(uint32_t id) : + id(id), + creatorTokenID(UNKNOWN_ENTITY_TOKEN), + isKnownID(true) +{ + //qDebug() << "EntityItemID::EntityItemID(uint32_t id)... isKnownID=" + // << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; +}; + +uint32_t EntityItemID::getIDfromCreatorTokenID(uint32_t creatorTokenID) { + if (_tokenIDsToIDs.find(creatorTokenID) != _tokenIDsToIDs.end()) { + return _tokenIDsToIDs[creatorTokenID]; + } + return UNKNOWN_ENTITY_ID; +} + +uint32_t EntityItemID::getNextCreatorTokenID() { + uint32_t creatorTokenID = _nextCreatorTokenID; + _nextCreatorTokenID++; + return creatorTokenID; +} + +EntityItemID EntityItemID::assignActualIDForToken() const { + EntityItemID newlyAssignedEntityID; + + newlyAssignedEntityID.creatorTokenID = creatorTokenID; + newlyAssignedEntityID.isKnownID = true; + newlyAssignedEntityID.id = _nextID; + _nextID++; + + return newlyAssignedEntityID; +} + +EntityItemID EntityItemID::convertToKnownIDVersion() const { + EntityItemID knownIDVersionEntityID; + + knownIDVersionEntityID.creatorTokenID = UNKNOWN_ENTITY_TOKEN; + knownIDVersionEntityID.isKnownID = true; + knownIDVersionEntityID.id = id; + + return knownIDVersionEntityID; +} + +EntityItemID EntityItemID::convertToCreatorTokenVersion() const { + EntityItemID knownIDVersionEntityID; + + knownIDVersionEntityID.creatorTokenID = creatorTokenID; + knownIDVersionEntityID.isKnownID = false; + knownIDVersionEntityID.id = UNKNOWN_ENTITY_ID; + + return knownIDVersionEntityID; +} + +void EntityItemID::handleAddEntityResponse(const QByteArray& packet) { + +qDebug() << "EntityItemID::handleAddEntityResponse()..."; + + const unsigned char* dataAt = reinterpret_cast(packet.data()); + int numBytesPacketHeader = numBytesForPacketHeader(packet); + dataAt += numBytesPacketHeader; + + uint32_t creatorTokenID; + memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID)); + dataAt += sizeof(creatorTokenID); + + uint32_t entityItemID; + memcpy(&entityItemID, dataAt, sizeof(entityItemID)); + dataAt += sizeof(entityItemID); + +qDebug() << "EntityItemID::handleAddEntityResponse()... entityItemID=" << entityItemID << "creatorTokenID=" << creatorTokenID; + + // add our token to id mapping + _tokenIDsToIDs[creatorTokenID] = entityItemID; +} + QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) { QScriptValue obj = engine->newObject(); obj.setProperty("id", id.id); diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 0675389da7..5ad5b4dc08 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -27,24 +27,31 @@ const uint32_t UNKNOWN_ENTITY_ID = 0xFFFFFFFF; /// correct mapping. This class works with the scripting API an allows the developer to edit models they created. class EntityItemID { public: - EntityItemID() : - id(NEW_ENTITY), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(false) { - //qDebug() << "EntityItemID::EntityItemID()... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; - }; - - EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) : - id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { - //qDebug() << "EntityItemID::EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; - }; - - EntityItemID(uint32_t id) : - id(id), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(true) { - //qDebug() << "EntityItemID::EntityItemID(uint32_t id)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; - }; + EntityItemID(); + EntityItemID(const EntityItemID& other); + EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID); + EntityItemID(uint32_t id); uint32_t id; uint32_t creatorTokenID; bool isKnownID; + + // these methods will reduce the ID down to half the IDs data to allow for comparisons and searches of known values + EntityItemID convertToKnownIDVersion() const; + EntityItemID convertToCreatorTokenVersion() const; + + // these methods allow you to create models, and later edit them. + static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID); + static uint32_t getNextCreatorTokenID(); + static void handleAddEntityResponse(const QByteArray& packet); + +private: + friend class EntityTree; + EntityItemID assignActualIDForToken() const; // only called by EntityTree + + static quint32 _nextID; + static uint32_t _nextCreatorTokenID; /// used by the static interfaces for creator token ids + static std::map _tokenIDsToIDs; }; inline bool operator<(const EntityItemID& a, const EntityItemID& b) { @@ -52,7 +59,7 @@ inline bool operator<(const EntityItemID& a, const EntityItemID& b) { } inline bool operator==(const EntityItemID& a, const EntityItemID& b) { - if (a.id == UNKNOWN_ENTITY_ID && b.id == UNKNOWN_ENTITY_ID) { + if (a.id == UNKNOWN_ENTITY_ID || b.id == UNKNOWN_ENTITY_ID) { return a.creatorTokenID == b.creatorTokenID; } return a.id == b.id; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 084150b495..2ca365740d 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -27,7 +27,7 @@ void EntityScriptingInterface::queueEntityMessage(PacketType packetType, EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { // The application will keep track of creatorTokenID - uint32_t creatorTokenID = EntityItem::getNextCreatorTokenID(); + uint32_t creatorTokenID = EntityItemID::getNextCreatorTokenID(); EntityItemID id(NEW_ENTITY, creatorTokenID, false ); @@ -48,7 +48,7 @@ EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) { uint32_t actualID = entityID.id; if (!entityID.isKnownID) { - actualID = EntityItem::getIDfromCreatorTokenID(entityID.creatorTokenID); + actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); if (actualID == UNKNOWN_ENTITY_ID) { return entityID; // bailing early } @@ -72,7 +72,10 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID _entityTree->lockForRead(); EntityItem* entity = const_cast(_entityTree->findEntityByID(identity.id, true)); if (entity) { - entity->setSittingPoints(_entityTree->getGeometryForEntity(*entity)->sittingPoints); + + // TODO: look into sitting points!!! + //entity->setSittingPoints(_entityTree->getGeometryForEntity(*entity)->sittingPoints); + results = entity->getProperties(); } else { results.setIsUnknownID(); @@ -90,7 +93,7 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E // if the model is unknown, attempt to look it up if (!entityID.isKnownID) { - actualID = EntityItem::getIDfromCreatorTokenID(entityID.creatorTokenID); + actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); } // if at this point, we know the id, send the update to the model server @@ -126,7 +129,7 @@ void EntityScriptingInterface::deleteEntity(EntityItemID entityID) { // if the model is unknown, attempt to look it up if (!entityID.isKnownID) { - actualID = EntityItem::getIDfromCreatorTokenID(entityID.creatorTokenID); + actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); } // if at this point, we know the id, send the update to the model server diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5939c975b4..3a7608eddc 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -371,9 +371,15 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp } EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { - qDebug() << "EntityTree::addEntity()... entityID=" << entityID; + // NOTE: This method is used in the client (isViewing) and the server tree. In the client (isViewing), it's possible to + // create EntityItems that do not yet have known IDs. In the server tree however we don't want to have entities without + // known IDs. + if (!getIsViewing()) { + assert(entityID.isKnownID); + } + EntityItem* result = NULL; // You should not call this on existing entities that are already part of the tree! Call updateEntity() EntityTreeElement* containingElement = getContainingElement(entityID); @@ -550,7 +556,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID) { bool wantDebug = false; if (wantDebug) { EntityTreeElement* containingElement = getContainingElement(entityID); - qDebug() << "EntityTree::storeEntity().... after store... containingElement=" << containingElement; + qDebug() << "EntityTree::deleteEntity().... after delete... containingElement=" << containingElement; } } @@ -575,34 +581,157 @@ void EntityTree::deleteEntities(QSet entityIDs) { } } -// scans the tree and handles mapping locally created entities to know IDs. -// in the event that this tree is also viewing the scene, then we need to also -// search the tree to make sure we don't have a duplicate entity from the viewing -// operation. -bool EntityTree::findAndUpdateEntityItemIDOperation(OctreeElement* element, void* extraData) { - bool keepSearching = true; +/// This class is used to recurse the tree and find and fix entity IDs that are shifting from creator token based to +/// known ID based entity IDs. This should only be used on a client side (viewing) tree. The typical usage is that +/// a local editor has been creating entities in the local tree, those entities have creatorToken based entity IDs. +/// But those entity edits are also sent up to the server, and the server eventually sends back to the client two +/// messages that can come in varying order. The first message would be a typical query/viewing data message conversation +/// in which the viewer "sees" the newly created entity. Those entities that have been seen, will have the authoritative +/// "known ID". Therefore there is a potential that there can be two copies of the same entity in the tree: +/// the "local only" "creator token" version of the entity and the "seen" "knownID" version of the entity. +/// The server also sends an "entityAdded" message to the client which contains the mapping of the creator token to +/// the known ID. These messages can come in any order, so we need to handle the follow cases: +/// +/// Case A: The local edit occurs, the addEntity message arrives, the "viewed data" has not yet arrived. +/// In this case, we can expect that our local tree has only one copy of the entity (the creator token), +/// and we only really need to fix up that entity with a new version of the ID that includes the knownID +/// +/// Case B: The local edit occurs, the "viewed data" for the new entity arrives, then the addEntity message arrives. +/// In this case, we can expect that our local tree has two copies of the entity (the creator token, and the +/// known ID version). We end up with two version of the entity because the server sends viewers only the +/// known ID version without a creator token. And we don't yet know the mapping until we get the mapping message. +/// In this case we need to fix up that entity with a new version of the ID that includes the knownID and +/// we need to delete the extra copy of the entity. +/// +/// This operator handles both of these cases. +class UpdateEntityIDOperator : public RecurseOctreeOperator { +public: + UpdateEntityIDOperator(EntityTree* tree, const EntityItemID& searchEntityID); + virtual bool PreRecursion(OctreeElement* element); + virtual bool PostRecursion(OctreeElement* element); +private: + EntityTree* _tree; + EntityItemID _entityIDKnownID; + EntityItemID _entityIDCreatorToken; + EntityTreeElement* _containingElementKnownID; + AACube _containingElementCubeKnownID; + EntityTreeElement* _containingElementCreatorToken; + AACube _containingElementCubeCreatorToken; + bool _entityFoundKnownID; + bool _entityFoundCreatorToken; +}; - FindAndUpdateEntityItemIDArgs* args = static_cast(extraData); - EntityTreeElement* entityTreeElement = static_cast(element); - - // Note: updateEntityItemID() will only operate on correctly found entities - entityTreeElement->updateEntityItemID(args); - - // if we've found and replaced both the creatorTokenID and the viewedEntity, then we - // can stop looking, otherwise we will keep looking - if (args->creatorTokenFound && args->viewedEntityFound) { - keepSearching = false; - } +UpdateEntityIDOperator::UpdateEntityIDOperator(EntityTree* tree, const EntityItemID& searchEntityID) : + _tree(tree), + _entityIDKnownID(searchEntityID.convertToKnownIDVersion()), + _entityIDCreatorToken(searchEntityID.convertToCreatorTokenVersion()), + _containingElementKnownID(_tree->getContainingElement(_entityIDKnownID)), + _containingElementCubeKnownID(), + _containingElementCreatorToken(_tree->getContainingElement(_entityIDCreatorToken)), + _containingElementCubeCreatorToken(), + _entityFoundKnownID(false), + _entityFoundCreatorToken(false) +{ + qDebug() << "UpdateEntityIDOperator::UpdateEntityIDOperator()..."; + qDebug() << " searchEntityID=" << searchEntityID; + qDebug() << " _entityIDKnownID=" << _entityIDKnownID; + qDebug() << " _entityIDCreatorToken=" << _entityIDCreatorToken; + qDebug() << " _containingElementKnownID=" << _containingElementKnownID; + qDebug() << " _containingElementCreatorToken=" << _containingElementCreatorToken; - return keepSearching; + if (_containingElementKnownID) { + _containingElementCubeKnownID = _containingElementKnownID->getAACube(); + } else { + _entityFoundKnownID = true; // entity doesn't exist... by definition it's updated + } + + if (_containingElementCreatorToken) { + _containingElementCubeCreatorToken = _containingElementCreatorToken->getAACube(); + } else { + _entityFoundCreatorToken = true; // entity doesn't exist... by definition it's updated + } + + qDebug() << " _containingElementCubeKnownID=" << _containingElementCubeKnownID; + qDebug() << " _containingElementCubeCreatorToken=" << _containingElementCubeCreatorToken; + qDebug() << " _entityFoundKnownID=" << _entityFoundKnownID; + qDebug() << " _entityFoundCreatorToken=" << _entityFoundCreatorToken; + +} + +bool UpdateEntityIDOperator::PreRecursion(OctreeElement* element) { + EntityTreeElement* entityTreeElement = static_cast(element); + + // In Pre-recursion, we're generally deciding whether or not we want to recurse this + // path of the tree. For this operation, we want to recurse the branch of the tree if + // and of the following are true: + // * We have not yet found the KnownID version of the entity, and this branch contains that entity + // * We have not yet found the CreatorToken version of the entity, and this branch contains that entity + // + // Note: it's often the case that the branch in question contains both versions of the entity + + bool keepSearching = false; // assume we don't need to search this branch of the tree any more + + // If we haven't yet found the creator token version entity, and this sub tree contains it then we need to keep searching. + if (!_entityFoundCreatorToken && element->getAACube().contains(_containingElementCubeCreatorToken)) { + + qDebug() << "recursing tree for creator token"; + + // If this is the element we're looking for, then ask it to update the entity's ID accordingly + if (element == _containingElementCreatorToken) { + qDebug() << "FOUND creator token entity"; + qDebug() << "FOUND creator token entity... entityTreeElement->updateEntityItemID()..."; + entityTreeElement->updateEntityItemID(_entityIDCreatorToken, _entityIDKnownID); + qDebug() << "FOUND creator token entity... _tree->setContainingElement(_entityIDCreatorToken, NULL);..."; + _tree->setContainingElement(_entityIDCreatorToken, NULL); + qDebug() << "FOUND creator token entity... _tree->setContainingElement(_entityIDKnownID, entityTreeElement);..."; + _tree->setContainingElement(_entityIDKnownID, entityTreeElement); + _entityFoundCreatorToken = true; + } + + // if we haven't found this version keep searching, but only in the case where this tree contains it + keepSearching = !_entityFoundCreatorToken; + } + + // If we haven't yet found the knownID version entity, and this sub tree contains our entity then we need to keep searching. + if (!_entityFoundKnownID && element->getAACube().contains(_containingElementCubeKnownID)) { + + // If this is the element we're looking for, then ask it to update the entity's ID accordingly + if (element == _containingElementKnownID) { + qDebug() << "FOUND known ID entity"; + qDebug() << "FOUND known ID entity... entityTreeElement->removeEntityWithEntityItemID(_entityIDKnownID)..."; + entityTreeElement->removeEntityWithEntityItemID(_entityIDKnownID); + qDebug() << "FOUND known ID entity... entityTreeElement->setContainingElement(_entityIDKnownID, NULL)..."; + _tree->setContainingElement(_entityIDKnownID, NULL); + + _entityFoundKnownID = true; + } + + // if we haven't found this version keep searching, but only in the case where this tree contains it + keepSearching = keepSearching || !_entityFoundKnownID; + } + + return keepSearching; // if we haven't yet found it, keep looking +} + +bool UpdateEntityIDOperator::PostRecursion(OctreeElement* element) { + // Post-recursion is the unwinding process. For this operation, while we + // unwind we want to mark the path as being dirty if we changed it below. + bool keepSearching = !_entityFoundKnownID || !_entityFoundCreatorToken; + + // As we unwind, if we're in either of these two paths, we mark our element + // as dirty. + // If we haven't yet found the knownID version entity, and this sub tree contains our entity then we need to keep searching. + if (element->getAACube().contains(_containingElementCubeKnownID) || + element->getAACube().contains(_containingElementCubeCreatorToken)) { + element->markWithChangedTime(); + } + return keepSearching; // if we haven't yet found it, keep looking } void EntityTree::handleAddEntityResponse(const QByteArray& packet) { - const bool wantDebug = false; + assert(getIsViewing()); // we should only call this on viewing trees (client side) - if (wantDebug) { - qDebug() << "EntityTree::handleAddEntityResponse()..."; - } + const bool wantDebug = true; int numBytesPacketHeader = numBytesForPacketHeader(packet); @@ -616,28 +745,24 @@ void EntityTree::handleAddEntityResponse(const QByteArray& packet) { memcpy(&entityID, dataAt, sizeof(entityID)); dataAt += sizeof(entityID); + // TODO: do we want callers to lock the tree before using this method??? + + // First, look for the existing entity in the tree.. + EntityItemID searchEntityID; + searchEntityID.id = entityID; + searchEntityID.creatorTokenID = creatorTokenID; + + lockForWrite(); + if (wantDebug) { + qDebug() << "EntityTree::handleAddEntityResponse()..."; qDebug() << " creatorTokenID=" << creatorTokenID; qDebug() << " entityID=" << entityID; + qDebug() << " searchEntityID=" << searchEntityID; } - // update entities in our tree - bool assumeEntityFound = !getIsViewing(); // if we're not a viewing tree, then we don't have to find the actual entity - FindAndUpdateEntityItemIDArgs args = { - entityID, - creatorTokenID, - false, - assumeEntityFound, - getIsViewing() - }; - - if (wantDebug) { - qDebug() << "looking for creatorTokenID=" << creatorTokenID << " entityID=" << entityID - << " getIsViewing()=" << getIsViewing(); - } - lockForWrite(); - // TODO: Switch this to use list of known entity IDs.... - recurseTreeWithOperation(findAndUpdateEntityItemIDOperation, &args); + UpdateEntityIDOperator theOperator(this, searchEntityID); + recurseTreeWithOperator(&theOperator); unlock(); } @@ -785,9 +910,19 @@ EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) / return foundEntity; } +EntityItemID EntityTree::assignEntityID(const EntityItemID& entityItemID) { + assert(!getIsViewing()); // NOTE: this only operates on an server (!isViewing) tree. + assert(!getContainingElement(entityItemID)); // NOTE: don't call this for existing entityIDs + + // The EntityItemID is responsible for assigning actual IDs and keeping track of them. + return entityItemID.assignActualIDForToken(); +} + int EntityTree::processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { + assert(!getIsViewing()); // NOTE: this only operates on an server (!isViewing) tree. + int processedBytes = 0; // we handle these types of "edit" packets switch (packetType) { @@ -803,13 +938,22 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char // If we got a valid edit packet, then it could be a new entity or it could be an update to // an existing entity... handle appropriately if (validEditPacket) { - // search for the entity by EntityItemID - EntityItem* existingEntity = findEntityByEntityItemID(entityItemID); - - // if the entityItem exists, then update it - if (existingEntity) { - updateEntity(entityItemID, properties); + + // If this is a knownID, then it should exist in our tree + if (entityItemID.isKnownID) { + // search for the entity by EntityItemID + EntityItem* existingEntity = findEntityByEntityItemID(entityItemID); + + // if the entityItem exists, then update it + if (existingEntity) { + updateEntity(entityItemID, properties); + } else { + qDebug() << "User attempted to edit an unknown entity."; + } } else { + // this is a new entity... assign a new entityID + entityItemID = assignEntityID(entityItemID); + EntityItem* newEntity = addEntity(entityItemID, properties); if (newEntity) { notifyNewlyCreatedEntity(*newEntity, senderNode); @@ -1084,10 +1228,41 @@ EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityIt // TODO: do we need to make this thread safe? Or is it acceptable as is if (_entityToElementMap.contains(entityItemID)) { return _entityToElementMap.value(entityItemID); + } else if (entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){ + // check the creator token version too... + qDebug() << "EntityTree::getContainingElement() checking the creator token..."; + + EntityItemID creatorTokenOnly; + creatorTokenOnly.id = UNKNOWN_ENTITY_ID; + creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID; + creatorTokenOnly.isKnownID = false; + + if (_entityToElementMap.contains(entityItemID)) { + qDebug() << "EntityTree::getContainingElement() found as creator token..."; + return _entityToElementMap.value(entityItemID); + } + } return NULL; } +// TODO: do we need to make this thread safe? Or is it acceptable as is +void EntityTree::resetContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element) { + assert(entityItemID.id != UNKNOWN_ENTITY_ID); + assert(entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN); + assert(element); + + // remove the old version with the creatorTokenID + EntityItemID creatorTokenVersion; + creatorTokenVersion.id = UNKNOWN_ENTITY_ID; + creatorTokenVersion.isKnownID = false; + creatorTokenVersion.creatorTokenID = entityItemID.creatorTokenID; + _entityToElementMap.remove(creatorTokenVersion); + + // set the new version with both creator token and real ID + _entityToElementMap[entityItemID] = element; +} + void EntityTree::setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element) { // TODO: do we need to make this thread safe? Or is it acceptable as is if (element) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 72eb8b27ac..943e80bd7c 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -56,6 +56,7 @@ public: EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties); bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties); + EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID // the old API void storeEntity(const EntityItem& entity, const SharedNodePointer& senderNode = SharedNodePointer()); @@ -98,6 +99,7 @@ public: EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) /*const*/; void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element); + void resetContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element); void debugDumpMap(); void dumpTree(); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 4f932c82ab..f78ece0642 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -427,6 +427,35 @@ bool EntityTreeElement::addOrUpdateEntity(EntityItem* entity, const EntityItemPr return true; } +// TODO: do we need to handle "killing" viewed entities as well??? +void EntityTreeElement::updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID) { + bool wantDebug = true; + + if (wantDebug) { + qDebug() << "EntityTreeElement::updateEntityItemID()... LOOKING FOR entity: " << + "creatorTokenEntityID=" << creatorTokenEntityID << + "knownIDEntityID=" << knownIDEntityID; + } + + uint16_t numberOfEntities = _entityItems->size(); + for (uint16_t i = 0; i < numberOfEntities; i++) { + EntityItem* thisEntity = (*_entityItems)[i]; + + EntityItemID thisEntityID = thisEntity->getEntityItemID(); + + if (thisEntityID == creatorTokenEntityID) { + if (wantDebug) { + qDebug() << "EntityTreeElement::updateEntityItemID()... FOUND IT entity: " << + "thisEntityID=" << thisEntityID << + "creatorTokenEntityID=" << creatorTokenEntityID << + "knownIDEntityID=" << knownIDEntityID; + } + thisEntity->setID(knownIDEntityID.id); + } + } +} + +/* void EntityTreeElement::updateEntityItemID(FindAndUpdateEntityItemIDArgs* args) { bool wantDebug = false; uint16_t numberOfEntities = _entityItems->size(); @@ -466,6 +495,7 @@ void EntityTreeElement::updateEntityItemID(FindAndUpdateEntityItemIDArgs* args) } } } +*/ const EntityItem* EntityTreeElement::getClosestEntity(glm::vec3 position) const { const EntityItem* closestEntity = NULL; diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index a2a58c4d16..ae2e73da4b 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -125,7 +125,8 @@ public: void addEntityItem(EntityItem* entity); - void updateEntityItemID(FindAndUpdateEntityItemIDArgs* args); + //void updateEntityItemID(FindAndUpdateEntityItemIDArgs* args); + void updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID); const EntityItem* getClosestEntity(glm::vec3 position) const; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 892f758f39..316f436e2c 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -76,6 +76,9 @@ EntityItem* EntityTypes::constructEntityItem(EntityType_t entityType, const Enti } EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead) { + +qDebug() << "EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead).... CALLED BUT NOT IMPLEMENTED!!!"; + return NULL; // TODO Implement this for real! }