mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
refactoring EntityItemID management and id assignment to be part of EntityItemID class instead of EntityItem class
This commit is contained in:
parent
1125cb183e
commit
0c7afc39ca
13 changed files with 420 additions and 107 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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!
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue