// // EntityTree.cpp // 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 // #include "EntityTree.h" EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) { _rootElement = createNewElement(); } EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) { EntityTreeElement* newElement = new EntityTreeElement(octalCode); newElement->setTree(this); return newElement; } void EntityTree::eraseAllOctreeElements() { _entityToElementMap.clear(); Octree::eraseAllOctreeElements(); } bool EntityTree::handlesEditPacketType(PacketType packetType) const { // we handle these types of "edit" packets switch (packetType) { case PacketTypeEntityAddOrEdit: case PacketTypeEntityErase: return true; default: return false; } } /// Give an EntityItemID and EntityItemProperties, this will either find the correct entity that already exists /// in the tree or it will create a new entity of the type specified by the properties and return that item. /// In the case that it creates a new item, the item will be properly added to the tree and all appropriate lookup hashes. EntityItem* EntityTree::getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* result = NULL; // we need to first see if we already have the entity in our tree by finding the containing element of the entity EntityTreeElement* containingElement = getContainingElement(entityID); if (containingElement) { result = containingElement->getEntityWithEntityItemID(entityID); } // if the element does not exist, then create a new one of the specified type... if (!result) { result = addEntity(entityID, properties); } return result; } class AddEntityOperator : public RecurseOctreeOperator { public: AddEntityOperator(EntityTree* tree, EntityItem* newEntity); virtual bool PreRecursion(OctreeElement* element); virtual bool PostRecursion(OctreeElement* element); virtual OctreeElement* PossiblyCreateChildAt(OctreeElement* element, int childIndex); private: EntityTree* _tree; EntityItem* _newEntity; bool _foundNew; quint64 _changeTime; AABox _newEntityBox; }; AddEntityOperator::AddEntityOperator(EntityTree* tree, EntityItem* newEntity) : _tree(tree), _newEntity(newEntity), _foundNew(false), _changeTime(usecTimestampNow()), _newEntityBox() { // caller must have verified existence of newEntity assert(_newEntity); _newEntityBox = _newEntity->getAACube().clamp(0.0f, 1.0f); qDebug() << "AddEntityOperator::AddEntityOperator() newEntity=" << newEntity; qDebug() << " _newEntityBox=" << _newEntityBox; } bool AddEntityOperator::PreRecursion(OctreeElement* element) { EntityTreeElement* entityTreeElement = static_cast(element); qDebug() << "AddEntityOperator::PreRecursion() entityTreeElement=" << entityTreeElement; entityTreeElement->debugDump(); // 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 location for the new entity, and this branch contains the bounds of the new entity bool keepSearching = false; // assume we don't need to search any more // If we haven't yet found the new entity, and this subTreeContains our new // entity, then we need to keep searching. if (!_foundNew && element->getAACube().contains(_newEntityBox)) { qDebug() << "this element contains the _newEntityBox..." << _newEntityBox; // If this element is the best fit for the new entity properties, then add/or update it if (entityTreeElement->bestFitBounds(_newEntityBox)) { qDebug() << "this element is the best fit for _newEntityBox..." << _newEntityBox; qDebug() << "calling entityTreeElement->addEntityItem(_newEntity);"; entityTreeElement->addEntityItem(_newEntity); qDebug() << "calling setContainingElement();"; _tree->setContainingElement(_newEntity->getEntityItemID(), entityTreeElement); qDebug() << "AddEntityOperator calling setContainingElement... new entityID=" << _newEntity->getEntityItemID(); _tree->debugDumpMap(); _foundNew = true; keepSearching = false; } else { keepSearching = true; } } return keepSearching; // if we haven't yet found it, keep looking } bool AddEntityOperator::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. // We might have two paths, one for the old entity and one for the new entity. bool keepSearching = !_foundNew; // As we unwind, if we're in either of these two paths, we mark our element // as dirty. if ((_foundNew && element->getAACube().contains(_newEntityBox))) { element->markWithChangedTime(); } return keepSearching; // if we haven't yet found it, keep looking } OctreeElement* AddEntityOperator::PossiblyCreateChildAt(OctreeElement* element, int childIndex) { // If we're getting called, it's because there was no child element at this index while recursing. // We only care if this happens while still searching for the new entity location. // Check to see if if (!_foundNew) { int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityBox); if (childIndex == indexOfChildContainingNewEntity) { return element->addChildAtIndex(childIndex); } } return NULL; } /// Adds a new entity item to the tree void EntityTree::addEntityItem(EntityItem* entityItem) { // You should not call this on existing entities that are already part of the tree! Call updateEntity() EntityItemID entityID = entityItem->getEntityItemID(); EntityTreeElement* containingElement = getContainingElement(entityID); assert(containingElement == NULL); // don't call addEntityItem() on existing entity items // Recurse the tree and store the entity in the correct tree element //qDebug() << "about to call recurseTreeWithOperator(AddEntityOperator)..."; AddEntityOperator theOperator(this, entityItem); recurseTreeWithOperator(&theOperator); //qDebug() << "AFTER... about to call recurseTreeWithOperator(AddEntityOperator)..."; //debugDumpMap(); // check to see if we need to simulate this entity.. changeEntityState(entityItem, EntityItem::Static, entityItem->getSimulationState()); _isDirty = true; } class UpdateEntityOperator : public RecurseOctreeOperator { public: UpdateEntityOperator(EntityTree* tree, EntityTreeElement* containingElement, EntityItem* existingEntity, const EntityItemProperties& properties); virtual bool PreRecursion(OctreeElement* element); virtual bool PostRecursion(OctreeElement* element); virtual OctreeElement* PossiblyCreateChildAt(OctreeElement* element, int childIndex); private: EntityTree* _tree; EntityItem* _existingEntity; EntityTreeElement* _containingElement; EntityItemProperties _properties; EntityItemID _entityItemID; bool _foundOld; bool _foundNew; bool _removeOld; quint64 _changeTime; AACube _oldEntityCube; AACube _newEntityCube; bool subTreeContainsOldEntity(OctreeElement* element); bool subTreeContainsNewEntity(OctreeElement* element); }; UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree, EntityTreeElement* containingElement, EntityItem* existingEntity, const EntityItemProperties& properties) : _tree(tree), _existingEntity(existingEntity), _containingElement(containingElement), _properties(properties), _entityItemID(existingEntity->getEntityItemID()), _foundOld(false), _foundNew(false), _removeOld(false), _changeTime(usecTimestampNow()), _oldEntityCube(), _newEntityCube() { // caller must have verified existence of containingElement and oldEntity assert(_containingElement && _existingEntity); _oldEntityCube = _existingEntity->getAACube(); // If the new properties has position OR radius changes, but not both, we need to // get the old property value and set it in our properties in order for our bounds // calculations to work. if (_properties.containsPositionChange() && !_properties.containsRadiusChange()) { float oldRadiusInMeters = _existingEntity->getRadius() * (float)TREE_SCALE; _properties.setRadius(oldRadiusInMeters); } if (!_properties.containsPositionChange() && _properties.containsRadiusChange()) { glm::vec3 oldPositionInMeters = _existingEntity->getPosition() * (float)TREE_SCALE; _properties.setPosition(oldPositionInMeters); } // If our new properties don't have bounds details (no change to position, etc) or if this containing element would // be the best fit for our new properties, then just do the new portion of the store pass, since the change path will // be the same for both parts of the update if (!_properties.containsBoundsProperties() || _containingElement->bestFitBounds(_properties)) { _foundOld = true; _newEntityCube = _oldEntityCube; } else { _newEntityCube = _properties.getAACubeTreeUnits(); _removeOld = true; // our properties are going to move us, so remember this for later processing } } // does this entity tree element contain the old entity bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElement* element) { return element->getAACube().contains(_oldEntityCube); } bool UpdateEntityOperator::subTreeContainsNewEntity(OctreeElement* element) { return element->getAACube().contains(_newEntityCube); } bool UpdateEntityOperator::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 old entity, and this branch contains our old entity // * We have not yet found the new entity, and this branch contains our new entity // // Note: it's often the case that the branch in question contains both the old entity // and the new entity. bool keepSearching = false; // assume we don't need to search any more // If we haven't yet found the old entity, and this subTreeContains our old // entity, then we need to keep searching. if (!_foundOld && subTreeContainsOldEntity(element)) { // If this is the element we're looking for, then ask it to remove the old entity // and we can stop searching. if (entityTreeElement == _containingElement) { // If the containgElement IS NOT the best fit for the new entity properties // then we need to remove it, and the updateEntity below will store it in the // correct element. if (_removeOld) { entityTreeElement->removeEntityItem(_existingEntity); // NOTE: only removes the entity, doesn't delete it // If we haven't yet found the new location, then we need to // make sure to remove our entity to element map, because for // now we're not in that map if (!_foundNew) { _tree->setContainingElement(_entityItemID, NULL); } } _foundOld = true; } else { // if this isn't the element we're looking for, then keep searching keepSearching = true; } } // If we haven't yet found the new entity, and this subTreeContains our new // entity, then we need to keep searching. if (!_foundNew && subTreeContainsNewEntity(element)) { // If this element is the best fit for the new entity properties, then add/or update it if (entityTreeElement->bestFitBounds(_newEntityCube)) { // if we are the existing containing element, then we can just do the update of the entity properties if (entityTreeElement == _containingElement) { assert(!_removeOld); // We shouldn't be in a remove old case and also be the new best fit // set the entity properties and mark our element as changed. _existingEntity->setProperties(_properties); } else { // otherwise, this is an add case. entityTreeElement->addEntityItem(_existingEntity); _existingEntity->setProperties(_properties); // still need to update the properties! _tree->setContainingElement(_entityItemID, entityTreeElement); } _foundNew = true; // we found the new item! } else { keepSearching = true; } } return keepSearching; // if we haven't yet found it, keep looking } bool UpdateEntityOperator::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. // We might have two paths, one for the old entity and one for the new entity. bool keepSearching = !_foundOld || !_foundNew; // As we unwind, if we're in either of these two paths, we mark our element // as dirty. if ((_foundOld && subTreeContainsOldEntity(element)) || (_foundNew && subTreeContainsNewEntity(element))) { element->markWithChangedTime(); } return keepSearching; // if we haven't yet found it, keep looking } OctreeElement* UpdateEntityOperator::PossiblyCreateChildAt(OctreeElement* element, int childIndex) { // If we're getting called, it's because there was no child element at this index while recursing. // We only care if this happens while still searching for the new entity location. // Check to see if if (!_foundNew) { int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityCube); if (childIndex == indexOfChildContainingNewEntity) { return element->addChildAtIndex(childIndex); } } return NULL; } bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { // You should not call this on existing entities that are already part of the tree! Call updateEntity() EntityTreeElement* containingElement = getContainingElement(entityID); if (!containingElement) { assert(containingElement); // don't call updateEntity() on entity items that don't exist return false; } EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID); if (!existingEntity) { assert(existingEntity); // don't call updateEntity() on entity items that don't exist return false; } // check to see if we need to simulate this entity... EntityItem::SimuationState oldState = existingEntity->getSimulationState(); UpdateEntityOperator theOperator(this, containingElement, existingEntity, properties); recurseTreeWithOperator(&theOperator); _isDirty = true; EntityItem::SimuationState newState = existingEntity->getSimulationState(); changeEntityState(existingEntity, oldState, newState); containingElement = getContainingElement(entityID); if (!containingElement) { qDebug() << "after updateEntity() we no longer have a containing element???"; assert(containingElement); // don't call updateEntity() on entity items that don't exist } return true; } EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { // NOTE: This method is used in the client and the server tree. In the client, 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 (getIsServer() && !entityID.isKnownID) { //assert(entityID.isKnownID); qDebug() << "UNEXPECTED!!! ----- EntityTree::addEntity()... (getIsSever() && !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); if (containingElement) { qDebug() << "UNEXPECTED!!! ----- EntityTree::addEntity()... entityID=" << entityID << "containingElement=" << containingElement; assert(containingElement == NULL); // don't call addEntity() on existing entity items return result; } // construct the instance of the entity EntityTypes::EntityType type = properties.getType(); result = EntityTypes::constructEntityItem(type, entityID, properties); if (result) { // this does the actual adding of the entity addEntityItem(result); } return result; } class EntityToDeleteDetails { public: const EntityItem* entity; AACube cube; EntityTreeElement* containingElement; }; inline uint qHash(const EntityToDeleteDetails& a, uint seed) { return qHash(a.entity->getEntityItemID(), seed); } inline bool operator==(const EntityToDeleteDetails& a, const EntityToDeleteDetails& b) { return a.entity->getEntityItemID() == b.entity->getEntityItemID(); } class DeleteEntityOperator : public RecurseOctreeOperator { public: DeleteEntityOperator(EntityTree* tree); DeleteEntityOperator(EntityTree* tree, const EntityItemID& searchEntityID); void addEntityIDToDeleteList(const EntityItemID& searchEntityID); virtual bool PreRecursion(OctreeElement* element); virtual bool PostRecursion(OctreeElement* element); private: EntityTree* _tree; QSet _entitiesToDelete; quint64 _changeTime; int _foundCount; int _lookingCount; bool subTreeContainsSomeEntitiesToDelete(OctreeElement* element); }; DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree, const EntityItemID& searchEntityID) : _tree(tree), _changeTime(usecTimestampNow()), _foundCount(0), _lookingCount(0) { addEntityIDToDeleteList(searchEntityID); } DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree) : _tree(tree), _changeTime(usecTimestampNow()), _foundCount(0), _lookingCount(0) { } void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEntityID) { // check our tree, to determine if this entity is known EntityToDeleteDetails details; details.containingElement = _tree->getContainingElement(searchEntityID); if (details.containingElement) { details.entity = details.containingElement->getEntityWithEntityItemID(searchEntityID); if (!details.entity) { //assert(false); qDebug() << "that's UNEXPECTED, we got a _containingElement, but couldn't find the oldEntity!"; } else { details.cube = details.containingElement->getAACube(); _entitiesToDelete << details; _lookingCount++; } } } // does this entity tree element contain the old entity bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElement* element) { bool containsEntity = false; // If we don't have an old entity, then we don't contain the entity, otherwise // check the bounds if (_entitiesToDelete.size() > 0) { AACube elementCube = element->getAACube(); foreach(const EntityToDeleteDetails& details, _entitiesToDelete) { if (elementCube.contains(details.cube)) { containsEntity = true; break; // if it contains at least one, we're good to go } } } return containsEntity; } bool DeleteEntityOperator::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 old entity, and this branch contains our old entity // * We have not yet found the new entity, and this branch contains our new entity // // Note: it's often the case that the branch in question contains both the old entity // and the new entity. bool keepSearching = false; // assume we don't need to search any more // If we haven't yet found all the entities, and this sub tree contains at least one of our // entities, then we need to keep searching. if ((_foundCount < _lookingCount) && subTreeContainsSomeEntitiesToDelete(element)) { // check against each of our search entities foreach(const EntityToDeleteDetails& details, _entitiesToDelete) { // If this is the element we're looking for, then ask it to remove the old entity // and we can stop searching. if (entityTreeElement == details.containingElement) { // This is a good place to delete it!!! EntityItemID entityItemID = details.entity->getEntityItemID(); //qDebug() << "DeleteEntityOperator::PreRecursion() BEFORE entityTreeElement->removeEntityWithEntityItemID(); element=" << entityTreeElement << "id=" << entityItemID; entityTreeElement->removeEntityWithEntityItemID(entityItemID); //qDebug() << "DeleteEntityOperator::PreRecursion() AFTER entityTreeElement->removeEntityWithEntityItemID(); element=" << entityTreeElement << "id=" << entityItemID; _tree->setContainingElement(entityItemID, NULL); //qDebug() << "DeleteEntityOperator calling setContainingElement(NULL)... entityID=" << entityItemID; //_tree->debugDumpMap(); _foundCount++; } } // if we haven't found all of our search for entities, then keep looking keepSearching = (_foundCount < _lookingCount); } return keepSearching; // if we haven't yet found it, keep looking } bool DeleteEntityOperator::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. // We might have two paths, one for the old entity and one for the new entity. bool keepSearching = (_foundCount < _lookingCount); // As we unwind, if we're in either of these two paths, we mark our element // as dirty. if ((subTreeContainsSomeEntitiesToDelete(element))) { element->markWithChangedTime(); } return keepSearching; // if we haven't yet found it, keep looking } void EntityTree::deleteEntity(const EntityItemID& entityID) { // NOTE: callers must lock the tree before using this method //EntityTreeElement* containingElement = getContainingElement(entityID); //qDebug() << "EntityTree::deleteEntity().... BEFORE DELETE... containingElement=" << containingElement; //debugDumpMap(); // First, look for the existing entity in the tree.. DeleteEntityOperator theOperator(this, entityID); recurseTreeWithOperator(&theOperator); _isDirty = true; bool wantDebug = true; if (wantDebug) { EntityTreeElement* containingElement = getContainingElement(entityID); qDebug() << "EntityTree::deleteEntity().... after delete... containingElement=" << containingElement; } } void EntityTree::deleteEntities(QSet entityIDs) { // NOTE: callers must lock the tree before using this method DeleteEntityOperator theOperator(this); foreach(const EntityItemID& entityID, entityIDs) { // First, look for the existing entity in the tree.. theOperator.addEntityIDToDeleteList(entityID); } recurseTreeWithOperator(&theOperator); _isDirty = true; bool wantDebug = false; if (wantDebug) { foreach(const EntityItemID& entityID, entityIDs) { EntityTreeElement* containingElement = getContainingElement(entityID); qDebug() << "EntityTree::deleteEntities().... after delete... entityID=" << entityID << "containingElement=" << containingElement; } } } /// This method is used to 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 method handles both of these cases. /// /// NOTE: unlike some operations on the tree, this process does not mark the tree as being changed. This is because /// we're not changing the content of the tree, we're only changing the internal IDs that map entities from creator /// based to known IDs. This means we don't have to recurse the tree to mark the changed path as dirty. void EntityTree::handleAddEntityResponse(const QByteArray& packet) { //assert(getIsClient()); // we should only call this on client trees if (!getIsClient()) { qDebug() << "UNEXPECTED!!! EntityTree::handleAddEntityResponse() with !getIsClient() ***"; } const unsigned char* dataAt = reinterpret_cast(packet.data()); int numBytesPacketHeader = numBytesForPacketHeader(packet); int bytesRead = numBytesPacketHeader; dataAt += numBytesPacketHeader; uint32_t creatorTokenID; memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID)); dataAt += sizeof(creatorTokenID); bytesRead += sizeof(creatorTokenID); QUuid entityID = QUuid::fromRfc4122(packet.mid(bytesRead, NUM_BYTES_RFC4122_UUID)); dataAt += NUM_BYTES_RFC4122_UUID; // First, look for the existing entity in the tree.. EntityItemID searchEntityID; searchEntityID.id = entityID; searchEntityID.creatorTokenID = creatorTokenID; lockForWrite(); // find the creator token version, it's containing element, and the entity itself EntityItem* foundEntity = NULL; EntityItemID creatorTokenVersion = searchEntityID.convertToCreatorTokenVersion(); EntityItemID knownIDVersion = searchEntityID.convertToKnownIDVersion(); bool wantDebug = false; if (wantDebug) { qDebug() << "EntityTree::handleAddEntityResponse()..."; qDebug() << " creatorTokenID=" << creatorTokenID; qDebug() << " entityID=" << entityID; qDebug() << " searchEntityID=" << searchEntityID; qDebug() << " creatorTokenVersion=" << creatorTokenVersion; qDebug() << " knownIDVersion=" << knownIDVersion; } // First look for and find the "viewed version" of this entity... it's possible we got // the known ID version sent to us between us creating our local version, and getting this // remapping message. If this happened, we actually want to find and delete that version of // the entity. EntityTreeElement* knownIDVersionContainingElement = getContainingElement(knownIDVersion); if (wantDebug) { qDebug() << " knownIDVersionContainingElement=" << knownIDVersionContainingElement; } if (knownIDVersionContainingElement) { foundEntity = knownIDVersionContainingElement->getEntityWithEntityItemID(knownIDVersion); if (wantDebug) { qDebug() << " foundEntity=" << foundEntity; } if (foundEntity) { knownIDVersionContainingElement->removeEntityWithEntityItemID(knownIDVersion); setContainingElement(knownIDVersion, NULL); if (wantDebug) { qDebug() << " FOUND VIEWED VERSION, removing entity, resetting containing element"; } } } EntityTreeElement* creatorTokenContainingElement = getContainingElement(creatorTokenVersion); if (wantDebug) { qDebug() << " creatorTokenContainingElement=" << creatorTokenContainingElement; } if (creatorTokenContainingElement) { foundEntity = creatorTokenContainingElement->getEntityWithEntityItemID(creatorTokenVersion); if (wantDebug) { qDebug() << " foundEntity=" << foundEntity; } if (foundEntity) { creatorTokenContainingElement->updateEntityItemID(creatorTokenVersion, knownIDVersion); setContainingElement(creatorTokenVersion, NULL); setContainingElement(knownIDVersion, creatorTokenContainingElement); if (wantDebug) { qDebug() << " FOUND CREATOR VERSION, updating entity ID and resetting containing element"; } } } unlock(); } class FindNearPointArgs { public: glm::vec3 position; float targetRadius; bool found; const EntityItem* closestEntity; float closestEntityDistance; }; bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData) { FindNearPointArgs* args = static_cast(extraData); EntityTreeElement* entityTreeElement = static_cast(element); glm::vec3 penetration; bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); // If this entityTreeElement contains the point, then search it... if (sphereIntersection) { const EntityItem* thisClosestEntity = entityTreeElement->getClosestEntity(args->position); // we may have gotten NULL back, meaning no entity was available if (thisClosestEntity) { glm::vec3 entityPosition = thisClosestEntity->getPosition(); float distanceFromPointToEntity = glm::distance(entityPosition, args->position); // If we're within our target radius if (distanceFromPointToEntity <= args->targetRadius) { // we are closer than anything else we've found if (distanceFromPointToEntity < args->closestEntityDistance) { args->closestEntity = thisClosestEntity; args->closestEntityDistance = distanceFromPointToEntity; args->found = true; } } } // we should be able to optimize this... return true; // keep searching in case children have closer entities } // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching return false; } const EntityItem* EntityTree::findClosestEntity(glm::vec3 position, float targetRadius) { FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX }; lockForRead(); // NOTE: This should use recursion, since this is a spatial operation recurseTreeWithOperation(findNearPointOperation, &args); unlock(); return args.closestEntity; } class FindAllNearPointArgs { public: glm::vec3 position; float targetRadius; QVector entities; }; bool EntityTree::findInSphereOperation(OctreeElement* element, void* extraData) { FindAllNearPointArgs* args = static_cast(extraData); glm::vec3 penetration; bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); // If this element contains the point, then search it... if (sphereIntersection) { EntityTreeElement* entityTreeElement = static_cast(element); entityTreeElement->getEntities(args->position, args->targetRadius, args->entities); return true; // keep searching in case children have closer entities } // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching return false; } // NOTE: assumes caller has handled locking void EntityTree::findEntities(const glm::vec3& center, float radius, QVector& foundEntities) { FindAllNearPointArgs args = { center, radius }; // NOTE: This should use recursion, since this is a spatial operation recurseTreeWithOperation(findInSphereOperation, &args); // swap the two lists of entity pointers instead of copy foundEntities.swap(args.entities); } class FindEntitiesInCubeArgs { public: FindEntitiesInCubeArgs(const AACube& cube) : _cube(cube), _foundEntities() { } AACube _cube; QVector _foundEntities; }; bool EntityTree::findInCubeOperation(OctreeElement* element, void* extraData) { FindEntitiesInCubeArgs* args = static_cast(extraData); const AACube& elementCube = element->getAACube(); if (elementCube.touches(args->_cube)) { EntityTreeElement* entityTreeElement = static_cast(element); entityTreeElement->getEntities(args->_cube, args->_foundEntities); return true; } return false; } // NOTE: assumes caller has handled locking void EntityTree::findEntities(const AACube& cube, QVector foundEntities) { FindEntitiesInCubeArgs args(cube); // NOTE: This should use recursion, since this is a spatial operation recurseTreeWithOperation(findInCubeOperation, &args); // swap the two lists of entity pointers instead of copy foundEntities.swap(args._foundEntities); } EntityItem* EntityTree::findEntityByID(const QUuid& id) { EntityItemID entityID(id); bool wantDebug = false; if (wantDebug) { qDebug() << "EntityTree::findEntityByID()..."; qDebug() << " id=" << id; qDebug() << " entityID=" << entityID; qDebug() << "_entityToElementMap=" << _entityToElementMap; } return findEntityByEntityItemID(entityID); } EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) /*const*/ { EntityItem* foundEntity = NULL; EntityTreeElement* containingElement = getContainingElement(entityID); if (containingElement) { foundEntity = containingElement->getEntityWithEntityItemID(entityID); } return foundEntity; } EntityItemID EntityTree::assignEntityID(const EntityItemID& entityItemID) { assert(getIsServer()); // NOTE: this only operates on an server 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) { qDebug() << "EntityTree::processEditPacketData().... ******************"; assert(getIsServer()); // NOTE: this only operates on an server tree. int processedBytes = 0; // we handle these types of "edit" packets switch (packetType) { case PacketTypeEntityAddOrEdit: { qDebug() << "EntityTree::processEditPacketData()...."; EntityItemID entityItemID; EntityItemProperties properties; bool validEditPacket = EntityTypes::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties); // 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) { // 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 { // // NOTE: We need to fix this... we can't have the creator tokens in the server side map... because if we do that // then we will have multiple creator tokens with the same id from different editors... since assignEntityID() // checks for the ID already existing in the map, this will assert/abort. // // But we do want the creator tokens in the client side version of the map... // so maybe the fix is just to .... // this is a new entity... assign a new entityID qDebug() << "EntityTree::processEditPacketData() ... BEFORE assignEntityID()... entityItemID=" << entityItemID; entityItemID = assignEntityID(entityItemID); qDebug() << "EntityTree::processEditPacketData() ... AFTER assignEntityID()... entityItemID=" << entityItemID; EntityItem* newEntity = addEntity(entityItemID, properties); if (newEntity) { notifyNewlyCreatedEntity(*newEntity, senderNode); } } } break; } default: processedBytes = 0; break; } return processedBytes; } void EntityTree::notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode) { _newlyCreatedHooksLock.lockForRead(); for (size_t i = 0; i < _newlyCreatedHooks.size(); i++) { _newlyCreatedHooks[i]->entityCreated(newEntity, senderNode); } _newlyCreatedHooksLock.unlock(); } void EntityTree::addNewlyCreatedHook(NewlyCreatedEntityHook* hook) { _newlyCreatedHooksLock.lockForWrite(); _newlyCreatedHooks.push_back(hook); _newlyCreatedHooksLock.unlock(); } void EntityTree::removeNewlyCreatedHook(NewlyCreatedEntityHook* hook) { _newlyCreatedHooksLock.lockForWrite(); for (size_t i = 0; i < _newlyCreatedHooks.size(); i++) { if (_newlyCreatedHooks[i] == hook) { _newlyCreatedHooks.erase(_newlyCreatedHooks.begin() + i); break; } } _newlyCreatedHooksLock.unlock(); } bool EntityTree::updateOperation(OctreeElement* element, void* extraData) { EntityTreeUpdateArgs* args = static_cast(extraData); EntityTreeElement* entityTreeElement = static_cast(element); entityTreeElement->update(*args); return true; } bool EntityTree::pruneOperation(OctreeElement* element, void* extraData) { EntityTreeElement* entityTreeElement = static_cast(element); for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { EntityTreeElement* childAt = entityTreeElement->getChildAtIndex(i); /* if (childAt) { qDebug() << "consider pruning child" << i << "childAt=" << childAt << "isLeaf=" << (childAt ? childAt->isLeaf() : false) << "hasEntities=" << (childAt ? childAt->hasEntities() : false); } */ if (childAt && childAt->isLeaf() && !childAt->hasEntities()) { //qDebug() << "pruning child" << i; entityTreeElement->deleteChildAtIndex(i); } } return true; } void EntityTree::changeEntityState(EntityItem* const entity, EntityItem::SimuationState oldState, EntityItem::SimuationState newState) { qDebug() << "EntityTree::changeEntityState()...."; qDebug() << " oldState:" << oldState; qDebug() << " newState:" << newState; qDebug() << "EntityTree::changeEntityState() BEFORE...."; qDebug() << " _changingEntities:" << _changingEntities; qDebug() << " _movingEntities:" << _movingEntities; //if (oldState != newState) { switch (oldState) { case EntityItem::Changing: _changingEntities.removeAll(entity); break; case EntityItem::Moving: _movingEntities.removeAll(entity); break; default: break; } switch (newState) { case EntityItem::Changing: _changingEntities.push_back(entity); break; case EntityItem::Moving: _movingEntities.push_back(entity); break; default: break; } //} qDebug() << "EntityTree::changeEntityState() AFTER...."; qDebug() << " _changingEntities:" << _changingEntities; qDebug() << " _movingEntities:" << _movingEntities; } void EntityTree::update() { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // NEW CODE!!!! // // our new strategy should be to segregate entities into three classes: // 1) stationary things that are not changing - most models // 2) stationary things that are animating - they can be touched linearly and they don't change the tree // 3) moving things - these need to scan the tree and update accordingly lockForWrite(); quint64 now = usecTimestampNow(); //_movingEntities; // entities that are moving as part of update //_changingEntities; // entities that are changing (like animating), but not moving for (int i = 0; i < _changingEntities.size(); i++) { EntityItem* thisEntity = _changingEntities[i]; thisEntity->update(now); } unlock(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // OLD CODE!!!! // // The old update code scanned the entire tree... this is very expensive, since not all entities are getting simulated // // // XXXBHG: replace storeEntity with new API!! // //qDebug() << "EntityTree::update().... NOT YET IMPLEMENTED!!!"; // #if 0 ////////////////////////////////////////////////////// // // lockForWrite(); // _isDirty = true; // // // TODO: could we manage this by iterating the known entities map/hash? Would that be faster? // EntityTreeUpdateArgs args; // recurseTreeWithOperation(updateOperation, &args); // // // now add back any of the particles that moved elements.... // int movingEntities = args._movingEntities.size(); // // for (int i = 0; i < movingEntities; i++) { // // bool shouldDie = args._movingEntities[i]->getShouldBeDeleted(); // // // if the particle is still inside our total bounds, then re-add it // AACube treeBounds = getRoot()->getAACube(); // // if (!shouldDie && treeBounds.contains(args._movingEntities[i]->getPosition())) { // storeEntity(*args._movingEntities[i]); // } else { // uint32_t entityItemID = args._movingEntities[i]->getID(); // quint64 deletedAt = usecTimestampNow(); // _recentlyDeletedEntitiesLock.lockForWrite(); // _recentlyDeletedEntityItemIDs.insert(deletedAt, entityItemID); // _recentlyDeletedEntitiesLock.unlock(); // } // } // // // #endif // 0 ////////////////////////////////////////////////////// // // // // prune the tree... // /* // lockForWrite(); //qDebug() << "pruning tree"; // recurseTreeWithOperation(pruneOperation, NULL); // unlock(); // */ } bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { // we can probably leverage the ordered nature of QMultiMap to do this quickly... bool hasSomethingNewer = false; _recentlyDeletedEntitiesLock.lockForRead(); QMultiMap::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin(); while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) { //qDebug() << "considering... time/key:" << iterator.key(); if (iterator.key() > sinceTime) { //qDebug() << "YES newer... time/key:" << iterator.key(); hasSomethingNewer = true; } ++iterator; } _recentlyDeletedEntitiesLock.unlock(); return hasSomethingNewer; } // sinceTime is an in/out parameter - it will be side effected with the last time sent out bool EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* outputBuffer, size_t maxLength, size_t& outputLength) { qDebug() << "EntityTree::encodeEntitiesDeletedSince()"; bool hasMoreToSend = true; unsigned char* copyAt = outputBuffer; size_t numBytesPacketHeader = populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeEntityErase); copyAt += numBytesPacketHeader; outputLength = numBytesPacketHeader; // pack in flags OCTREE_PACKET_FLAGS flags = 0; OCTREE_PACKET_FLAGS* flagsAt = (OCTREE_PACKET_FLAGS*)copyAt; *flagsAt = flags; copyAt += sizeof(OCTREE_PACKET_FLAGS); outputLength += sizeof(OCTREE_PACKET_FLAGS); // pack in sequence number OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)copyAt; *sequenceAt = sequenceNumber; copyAt += sizeof(OCTREE_PACKET_SEQUENCE); outputLength += sizeof(OCTREE_PACKET_SEQUENCE); // pack in timestamp OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); OCTREE_PACKET_SENT_TIME* timeAt = (OCTREE_PACKET_SENT_TIME*)copyAt; *timeAt = now; copyAt += sizeof(OCTREE_PACKET_SENT_TIME); outputLength += sizeof(OCTREE_PACKET_SENT_TIME); uint16_t numberOfIds = 0; // placeholder for now unsigned char* numberOfIDsAt = copyAt; memcpy(copyAt, &numberOfIds, sizeof(numberOfIds)); copyAt += sizeof(numberOfIds); outputLength += sizeof(numberOfIds); // we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been // deleted since we last sent to this node _recentlyDeletedEntitiesLock.lockForRead(); QMultiMap::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin(); while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) { QList values = _recentlyDeletedEntityItemIDs.values(iterator.key()); for (int valueItem = 0; valueItem < values.size(); ++valueItem) { // if the timestamp is more recent then out last sent time, include it if (iterator.key() > sinceTime) { uint32_t entityID = values.at(valueItem); memcpy(copyAt, &entityID, sizeof(entityID)); copyAt += sizeof(entityID); outputLength += sizeof(entityID); numberOfIds++; // check to make sure we have room for one more id... if (outputLength + sizeof(uint32_t) > maxLength) { break; } } } // check to make sure we have room for one more id... if (outputLength + sizeof(uint32_t) > maxLength) { // let our caller know how far we got sinceTime = iterator.key(); break; } ++iterator; } // if we got to the end, then we're done sending if (iterator == _recentlyDeletedEntityItemIDs.constEnd()) { hasMoreToSend = false; } _recentlyDeletedEntitiesLock.unlock(); // replace the correct count for ids included memcpy(numberOfIDsAt, &numberOfIds, sizeof(numberOfIds)); return hasMoreToSend; } // called by the server when it knows all nodes have been sent deleted packets void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { //qDebug() << "forgetEntitiesDeletedBefore()"; QSet keysToRemove; _recentlyDeletedEntitiesLock.lockForWrite(); QMultiMap::iterator iterator = _recentlyDeletedEntityItemIDs.begin(); // First find all the keys in the map that are older and need to be deleted while (iterator != _recentlyDeletedEntityItemIDs.end()) { if (iterator.key() <= sinceTime) { keysToRemove << iterator.key(); } ++iterator; } // Now run through the keysToRemove and remove them foreach (quint64 value, keysToRemove) { //qDebug() << "removing the key, _recentlyDeletedEntityItemIDs.remove(value); time/key:" << value; _recentlyDeletedEntityItemIDs.remove(value); } _recentlyDeletedEntitiesLock.unlock(); } void EntityTree::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { #if 0 /////////////// qDebug() << "EntityTree::processEraseMessage()..."; const unsigned char* packetData = (const unsigned char*)dataByteArray.constData(); const unsigned char* dataAt = packetData; size_t packetLength = dataByteArray.size(); size_t numBytesPacketHeader = numBytesForPacketHeader(dataByteArray); size_t processedBytes = numBytesPacketHeader; dataAt += numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); uint16_t numberOfIds = 0; // placeholder for now memcpy(&numberOfIds, dataAt, sizeof(numberOfIds)); dataAt += sizeof(numberOfIds); processedBytes += sizeof(numberOfIds); if (numberOfIds > 0) { QSet entityItemIDsToDelete; for (size_t i = 0; i < numberOfIds; i++) { if (processedBytes + sizeof(uint32_t) > packetLength) { break; // bail to prevent buffer overflow } uint32_t entityID = 0; // placeholder for now memcpy(&entityID, dataAt, sizeof(entityID)); dataAt += sizeof(entityID); processedBytes += sizeof(entityID); EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; qDebug() << "EntityTree::processEraseMessage()... entityItemIDsToDelete << entityItemID=" << entityItemID; } qDebug() << "EntityTree::processEraseMessage()... deleteEntities(entityItemIDsToDelete)"; deleteEntities(entityItemIDsToDelete); } #endif // 0 /////////////// } EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ { //qDebug() << "_entityToElementMap=" << _entityToElementMap; bool wantDebug = false; if (wantDebug) { qDebug() << "EntityTree::getContainingElement() entityItemID=" << entityItemID; debugDumpMap(); } // 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... if (wantDebug) { 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)) { if (wantDebug) { 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 we're a sever side tree, we always remove the creator tokens from our map items EntityItemID storedEntityItemID = entityItemID; if (getIsServer()) { storedEntityItemID.creatorTokenID = UNKNOWN_ENTITY_TOKEN; } if (element) { _entityToElementMap[storedEntityItemID] = element; } else { _entityToElementMap.remove(storedEntityItemID); } bool wantDebug = false; if (wantDebug) { qDebug() << "setContainingElement() entityItemID=" << entityItemID << "storedEntityItemID=" << storedEntityItemID << "element=" << element; debugDumpMap(); //qDebug() << "AFTER _entityToElementMap=" << _entityToElementMap; } } void EntityTree::debugDumpMap() { qDebug() << "EntityTree::debugDumpMap() --------------------------"; QHashIterator i(_entityToElementMap); while (i.hasNext()) { i.next(); qDebug() << i.key() << ": " << i.value(); } qDebug() << "-----------------------------------------------------"; } class DebugOperator : public RecurseOctreeOperator { public: virtual bool PreRecursion(OctreeElement* element); virtual bool PostRecursion(OctreeElement* element) { return true; }; virtual OctreeElement* PossiblyCreateChildAt(OctreeElement* element, int childIndex) { return NULL; } }; bool DebugOperator::PreRecursion(OctreeElement* element) { EntityTreeElement* entityTreeElement = static_cast(element); entityTreeElement->debugDump(); return true; } void EntityTree::dumpTree() { // First, look for the existing entity in the tree.. DebugOperator theOperator; recurseTreeWithOperator(&theOperator); } void EntityTree::rememberDirtyCube(const AACube& cube) { // TODO: do something here qDebug() << "void EntityTree::rememberDirtyCube(const AACube& cube) CALLED BUT NOT IMPLEMENTED!"; } void EntityTree::rememberEntityToMove(const EntityItem* entity) { // TODO: do something here qDebug() << "void EntityTree::rememberEntityToMove() CALLED BUT NOT IMPLEMENTED!"; } void EntityTree::sendEntities(EntityEditPacketSender* packetSender, float x, float y, float z) { SendEntitiesOperationArgs args; args.packetSender = packetSender; args.root = glm::vec3(x, y, z); recurseTreeWithOperation(sendEntitiesOperation, &args); packetSender->releaseQueuedMessages(); } bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) { SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElement* entityTreeElement = static_cast(element); // TODO: implement this!! /** const QList& modelList = modelTreeElement->getModels(); for (int i = 0; i < modelList.size(); i++) { uint32_t creatorTokenID = ModelItem::getNextCreatorTokenID(); ModelItemID id(NEW_MODEL, creatorTokenID, false); ModelItemProperties properties; properties.copyFromNewModelItem(modelList.at(i)); properties.setPosition(properties.getPosition() + args->root); args->packetSender->queueModelEditMessage(PacketTypeModelAddOrEdit, id, properties); } **/ return true; }