EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead, ReadBitstreamToTreeParams& args) { if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { EntityItemID tempEntityID; EntityItemProperties tempProperties; return constructEntityItem(Model, tempEntityID, tempProperties); } // Header bytes // object ID [16 bytes] // ByteCountCoded(type code) [~1 byte] // last edited [8 bytes] // ByteCountCoded(last_edited to last_updated delta) [~1-8 bytes] // PropertyFlags<>( everything ) [1-2 bytes] // ~27-35 bytes... const int MINIMUM_HEADER_BYTES = 27; int bytesRead = 0; if (bytesToRead >= MINIMUM_HEADER_BYTES) { int originalLength = bytesToRead; QByteArray originalDataBuffer((const char*)data, originalLength); // id QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size QUuid actualID = QUuid::fromRfc4122(encodedID); bytesRead += encodedID.size(); // type QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size ByteCountCoded<quint32> typeCoder = encodedType; encodedType = typeCoder; // determine true length bytesRead += encodedType.size(); quint32 type = typeCoder; EntityTypes::EntityType entityType = (EntityTypes::EntityType)type; EntityItemID tempEntityID(actualID); EntityItemProperties tempProperties; tempProperties.setCreated(usecTimestampNow()); // this is temporary... return constructEntityItem(entityType, tempEntityID, tempProperties); } return NULL; }
EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* newEntityItem = NULL; EntityTypeFactory factory = NULL; if (entityType >= 0 && entityType <= LAST) { factory = _factories[entityType]; } if (factory) { // NOTE: if someone attempts to create an entity with properties that do not include a proper "created" time // then set the created time to now if (!properties.hasCreatedTime()) { EntityItemProperties mutableProperties = properties; mutableProperties.setCreated(usecTimestampNow()); newEntityItem = factory(entityID, mutableProperties); } else { newEntityItem = factory(entityID, properties); } } return newEntityItem; }
// TODO: // how to handle lastEdited? // how to handle lastUpdated? // consider handling case where no properties are included... we should just ignore this packet... // // TODO: Right now, all possible properties for all subclasses are handled here. Ideally we'd prefer // to handle this in a more generic way. Allowing subclasses of EntityItem to register their properties // // TODO: There's a lot of repeated patterns in the code below to handle each property. It would be nice if the property // registration mechanism allowed us to collapse these repeated sections of code into a single implementation that // utilized the registration table to shorten up and simplify this code. // // TODO: Implement support for paged properties, spanning MTU, and custom properties // // TODO: Implement support for script and visible properties. // bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, EntityItemID& entityID, EntityItemProperties& properties) { bool valid = false; const unsigned char* dataAt = data; processedBytes = 0; // the first part of the data is an octcode, this is a required element of the edit packet format, but we don't // actually use it, we do need to skip it and read to the actual data we care about. int octets = numberOfThreeBitSectionsInCode(data); int bytesToReadOfOctcode = bytesRequiredForCodeLength(octets); // we don't actually do anything with this octcode... dataAt += bytesToReadOfOctcode; processedBytes += bytesToReadOfOctcode; // Edit packets have a last edited time stamp immediately following the octcode. // NOTE: the edit times have been set by the editor to match out clock, so we don't need to adjust // these times for clock skew at this point. quint64 lastEdited; memcpy(&lastEdited, dataAt, sizeof(lastEdited)); dataAt += sizeof(lastEdited); processedBytes += sizeof(lastEdited); properties.setLastEdited(lastEdited); // NOTE: We intentionally do not send "created" times in edit messages. This is because: // 1) if the edit is to an existing entity, the created time can not be changed // 2) if the edit is to a new entity, the created time is the last edited time // encoded id QByteArray encodedID((const char*)dataAt, NUM_BYTES_RFC4122_UUID); // maximum possible size QUuid editID = QUuid::fromRfc4122(encodedID); dataAt += encodedID.size(); processedBytes += encodedID.size(); bool isNewEntityItem = (editID == NEW_ENTITY); if (isNewEntityItem) { // If this is a NEW_ENTITY, then we assume that there's an additional uint32_t creatorToken, that // we want to send back to the creator as an map to the actual id QByteArray encodedToken((const char*)dataAt, (bytesToRead - processedBytes)); ByteCountCoded<quint32> tokenCoder = encodedToken; quint32 creatorTokenID = tokenCoder; encodedToken = tokenCoder; // determine true bytesToRead dataAt += encodedToken.size(); processedBytes += encodedToken.size(); //newEntityItem.setCreatorTokenID(creatorTokenID); //newEntityItem._newlyCreated = true; entityID.id = NEW_ENTITY; entityID.creatorTokenID = creatorTokenID; entityID.isKnownID = false; valid = true; // created time is lastEdited time properties.setCreated(lastEdited); } else { entityID.id = editID; entityID.creatorTokenID = UNKNOWN_ENTITY_TOKEN; entityID.isKnownID = true; valid = true; // created time is lastEdited time properties.setCreated(USE_EXISTING_CREATED_TIME); } // Entity Type... QByteArray encodedType((const char*)dataAt, (bytesToRead - processedBytes)); ByteCountCoded<quint32> typeCoder = encodedType; quint32 entityTypeCode = typeCoder; properties.setType((EntityTypes::EntityType)entityTypeCode); encodedType = typeCoder; // determine true bytesToRead dataAt += encodedType.size(); processedBytes += encodedType.size(); // Update Delta - when was this item updated relative to last edit... this really should be 0 // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? // TODO: do properties need to handle lastupdated??? // last updated is stored as ByteCountCoded delta from lastEdited QByteArray encodedUpdateDelta((const char*)dataAt, (bytesToRead - processedBytes)); ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta; encodedUpdateDelta = updateDeltaCoder; // determine true bytesToRead dataAt += encodedUpdateDelta.size(); processedBytes += encodedUpdateDelta.size(); // TODO: Do we need this lastUpdated?? We don't seem to use it. //quint64 updateDelta = updateDeltaCoder; //quint64 lastUpdated = lastEdited + updateDelta; // don't adjust for clock skew since we already did that for lastEdited // Property Flags... QByteArray encodedPropertyFlags((const char*)dataAt, (bytesToRead - processedBytes)); EntityPropertyFlags propertyFlags = encodedPropertyFlags; dataAt += propertyFlags.getEncodedLength(); processedBytes += propertyFlags.getEncodedLength(); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(PROP_ROTATION, setRotation); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MASS, float, setMass); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_SCRIPT,setScript); READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); return valid; }