refactoring EntityItemID management and id assignment to be part of EntityItemID class instead of EntityItem class

This commit is contained in:
ZappoMan 2014-07-17 10:24:32 -07:00
parent 1125cb183e
commit 0c7afc39ca
13 changed files with 420 additions and 107 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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:

View file

@ -29,42 +29,6 @@
#include "EntityItem.h"
#include "EntityTree.h"
uint32_t EntityItem::_nextID = 0;
// for locally created models
std::map<uint32_t,uint32_t> 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<const unsigned char*>(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;

View file

@ -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<uint32_t,uint32_t> _tokenIDsToIDs;
*/
quint32 _id;
EntityTypes::EntityType_t _type;

View file

@ -12,8 +12,120 @@
#include <QtCore/QObject>
#include <QDebug>
#include <PacketHeaders.h>
#include "EntityItemID.h"
uint32_t EntityItemID::_nextID = 0; // TODO: should be changed to UUID
// for locally created models
std::map<uint32_t,uint32_t> 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<const unsigned char*>(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);

View file

@ -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<uint32_t,uint32_t> _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;

View file

@ -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<EntityItem*>(_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

View file

@ -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<EntityItemID> 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<FindAndUpdateEntityItemIDArgs*>(extraData);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(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<EntityTreeElement*>(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) {

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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!
}