void testPropertyFlags(uint32_t value) { EntityPropertyFlags original; original.clear(); auto enumSize = sizeof(EntityPropertyList); for (size_t i = 0; i < enumSize * 8; ++i) { original.setHasProperty((EntityPropertyList)i); } QByteArray encoded = original.encode(); #ifndef QT_NO_DEBUG int originalSize = encoded.size(); #endif for (size_t i = 0; i < enumSize; ++i) { encoded.append(qrand()); } EntityPropertyFlags decodeOld, decodeNew; { decodeOld.decode(encoded); Q_ASSERT(decodeOld == original); } { #ifndef QT_NO_DEBUG int decodeSize = decodeNew.decode((const uint8_t*)encoded.data(), encoded.size()); #endif Q_ASSERT(originalSize == decodeSize); Q_ASSERT(decodeNew == original); } }
void EntityItemProperties::debugDump() const { qDebug() << "EntityItemProperties..."; qDebug() << " _type=" << EntityTypes::getEntityTypeName(_type); qDebug() << " _id=" << _id; qDebug() << " _idSet=" << _idSet; qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z; qDebug() << " _dimensions=" << getDimensions(); qDebug() << " _modelURL=" << _modelURL; qDebug() << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); props.debugDumpBits(); }
int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { int bytesRead = 0; const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL); if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) { setCompoundShapeURL(""); } else if (args.bitstreamVersion == VERSION_ENTITIES_HAS_COLLISION_MODEL) { READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); } else { READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); } READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setAnimationURL); // Because we're using AnimationLoop which will reset the frame index if you change it's running state // we want to read these values in the order they appear in the buffer, but call our setters in an // order that allows AnimationLoop to preserve the correct frame rate. float animationFPS = getAnimationFPS(); float animationFrameIndex = getAnimationFrameIndex(); bool animationIsPlaying = getAnimationIsPlaying(); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) { if (animationIsPlaying != getAnimationIsPlaying()) { setAnimationIsPlaying(animationIsPlaying); } } if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) { setAnimationFPS(animationFPS); } if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) { setAnimationFrameIndex(animationFrameIndex); } READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); READ_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, QString, setAnimationSettings); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType); return bytesRead; }
int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { int bytesRead = 0; const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); // Because we're using AnimationLoop which will reset the frame index if you change it's running state // we want to read these values in the order they appear in the buffer, but call our setters in an // order that allows AnimationLoop to preserve the correct frame rate. float animationFPS = getAnimationFPS(); float animationFrameIndex = getAnimationFrameIndex(); bool animationIsPlaying = getAnimationIsPlaying(); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) { if (animationIsPlaying != getAnimationIsPlaying()) { setAnimationIsPlaying(animationIsPlaying); } } if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) { setAnimationFPS(animationFPS); } if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) { setAnimationFrameIndex(animationFrameIndex); } READ_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, QString, setAnimationSettings); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType); READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles); READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan); READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate); READ_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection); READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, setEmitStrength); READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, setLocalGravity); READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); return bytesRead; }
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) { EntityItemProperties results; if (_entityTree) { _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity)); if (entity) { if (desiredProperties.getHasProperty(PROP_POSITION) || desiredProperties.getHasProperty(PROP_ROTATION) || desiredProperties.getHasProperty(PROP_LOCAL_POSITION) || desiredProperties.getHasProperty(PROP_LOCAL_ROTATION)) { // if we are explicitly getting position or rotation, we need parent information to make sense of them. desiredProperties.setHasProperty(PROP_PARENT_ID); desiredProperties.setHasProperty(PROP_PARENT_JOINT_INDEX); } if (desiredProperties.isEmpty()) { // these are left out of EntityItem::getEntityProperties so that localPosition and localRotation // don't end up in json saves, etc. We still want them here, though. EncodeBitstreamParams params; // unknown desiredProperties = entity->getEntityProperties(params); desiredProperties.setHasProperty(PROP_LOCAL_POSITION); desiredProperties.setHasProperty(PROP_LOCAL_ROTATION); } results = entity->getProperties(desiredProperties); // TODO: improve sitting points and naturalDimensions in the future, // for now we've included the old sitting points model behavior for entity types that are models // we've also added this hack for setting natural dimensions of models if (entity->getType() == EntityTypes::Model) { const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity); if (geometry) { results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } } } }); } return convertLocationToScriptSemantics(results); }
void OctreeTests::propertyFlagsTests() { bool verbose = true; qDebug() << "FIXME: this test is broken and needs to be fixed."; qDebug() << "We're disabling this so that ALL_BUILD works"; return; if (verbose) { qDebug() << "******************************************************************************************"; } qDebug() << "OctreeTests::propertyFlagsTests()"; { if (verbose) { qDebug() << "Test 1: EntityProperties: using setHasProperty()"; } EntityPropertyFlags props; props.setHasProperty(PROP_VISIBLE); props.setHasProperty(PROP_POSITION); props.setHasProperty(PROP_RADIUS); props.setHasProperty(PROP_MODEL_URL); props.setHasProperty(PROP_COMPOUND_SHAPE_URL); props.setHasProperty(PROP_ROTATION); QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 13 })); } { if (verbose) { qDebug() << "Test 2: ExamplePropertyFlags: using setHasProperty()"; } EntityPropertyFlags props2; props2.setHasProperty(PROP_VISIBLE); props2.setHasProperty(PROP_ANIMATION_URL); props2.setHasProperty(PROP_ANIMATION_FPS); props2.setHasProperty(PROP_ANIMATION_FRAME_INDEX); props2.setHasProperty(PROP_ANIMATION_PLAYING); QByteArray encoded = props2.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); if (verbose) { qDebug() << "Test 2b: remove flag with setHasProperty() PROP_PAUSE_SIMULATION"; } encoded = props2.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 136, 30 })); } { if (verbose) { qDebug() << "Test 3: ExamplePropertyFlags: using | operator"; } ExamplePropertyFlags props; props = ExamplePropertyFlags(EXAMPLE_PROP_VISIBLE) | ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_URL) | ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_FPS) | ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_FRAME_INDEX) | ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_PLAYING) | ExamplePropertyFlags(EXAMPLE_PROP_PAUSE_SIMULATION); QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); if (verbose) { qDebug() << "Test 3b: remove flag with -= EXAMPLE_PROP_PAUSE_SIMULATION"; } props -= EXAMPLE_PROP_PAUSE_SIMULATION; encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 136, 30 })); } { if (verbose) { qDebug() << "Test 3c: ExamplePropertyFlags: using |= operator"; } ExamplePropertyFlags props; props |= EXAMPLE_PROP_VISIBLE; props |= EXAMPLE_PROP_ANIMATION_URL; props |= EXAMPLE_PROP_ANIMATION_FPS; props |= EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props |= EXAMPLE_PROP_ANIMATION_PLAYING; props |= EXAMPLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); } { if (verbose) { qDebug() << "Test 4: ExamplePropertyFlags: using + operator"; } ExamplePropertyFlags props; props = ExamplePropertyFlags(EXAMPLE_PROP_VISIBLE) + ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_URL) + ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_FPS) + ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_FRAME_INDEX) + ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_PLAYING) + ExamplePropertyFlags(EXAMPLE_PROP_PAUSE_SIMULATION); QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); } { if (verbose) { qDebug() << "Test 5: ExamplePropertyFlags: using += operator"; } ExamplePropertyFlags props; props += EXAMPLE_PROP_VISIBLE; props += EXAMPLE_PROP_ANIMATION_URL; props += EXAMPLE_PROP_ANIMATION_FPS; props += EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props += EXAMPLE_PROP_ANIMATION_PLAYING; props += EXAMPLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); } { if (verbose) { qDebug() << "Test 6: ExamplePropertyFlags: using = ... << operator"; } ExamplePropertyFlags props; props = ExamplePropertyFlags(EXAMPLE_PROP_VISIBLE) << ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_URL) << ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_FPS) << ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_FRAME_INDEX) << ExamplePropertyFlags(EXAMPLE_PROP_ANIMATION_PLAYING) << ExamplePropertyFlags(EXAMPLE_PROP_PAUSE_SIMULATION); QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); } { if (verbose) { qDebug() << "Test 7: ExamplePropertyFlags: using <<= operator"; } ExamplePropertyFlags props; props <<= EXAMPLE_PROP_VISIBLE; props <<= EXAMPLE_PROP_ANIMATION_URL; props <<= EXAMPLE_PROP_ANIMATION_FPS; props <<= EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props <<= EXAMPLE_PROP_ANIMATION_PLAYING; props <<= EXAMPLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); } { if (verbose) { qDebug() << "Test 8: ExamplePropertyFlags: using << enum operator"; } ExamplePropertyFlags props; props << EXAMPLE_PROP_VISIBLE; props << EXAMPLE_PROP_ANIMATION_URL; props << EXAMPLE_PROP_ANIMATION_FPS; props << EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props << EXAMPLE_PROP_ANIMATION_PLAYING; props << EXAMPLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); } { if (verbose) { qDebug() << "Test 9: ExamplePropertyFlags: using << flags operator "; } ExamplePropertyFlags props; ExamplePropertyFlags props2; props << EXAMPLE_PROP_VISIBLE; props << EXAMPLE_PROP_ANIMATION_URL; props << EXAMPLE_PROP_ANIMATION_FPS; props2 << EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props2 << EXAMPLE_PROP_ANIMATION_PLAYING; props2 << EXAMPLE_PROP_PAUSE_SIMULATION; props << props2; QByteArray encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 196, 15, 2 })); } { if (verbose) { qDebug() << "Test 10: ExamplePropertyFlags comparison"; } ExamplePropertyFlags propsA; if (verbose) { qDebug() << "!propsA:" << (!propsA) << "{ expect true }"; } QCOMPARE(!propsA, true); propsA << EXAMPLE_PROP_VISIBLE; propsA << EXAMPLE_PROP_ANIMATION_URL; propsA << EXAMPLE_PROP_ANIMATION_FPS; propsA << EXAMPLE_PROP_ANIMATION_FRAME_INDEX; propsA << EXAMPLE_PROP_ANIMATION_PLAYING; propsA << EXAMPLE_PROP_PAUSE_SIMULATION; QCOMPARE(!propsA, false); ExamplePropertyFlags propsB; propsB << EXAMPLE_PROP_VISIBLE; propsB << EXAMPLE_PROP_ANIMATION_URL; propsB << EXAMPLE_PROP_ANIMATION_FPS; propsB << EXAMPLE_PROP_ANIMATION_FRAME_INDEX; propsB << EXAMPLE_PROP_ANIMATION_PLAYING; propsB << EXAMPLE_PROP_PAUSE_SIMULATION; if (verbose) { qDebug() << "propsA == propsB:" << (propsA == propsB) << "{ expect true }"; qDebug() << "propsA != propsB:" << (propsA != propsB) << "{ expect false }"; } QCOMPARE(propsA == propsB, true); QCOMPARE(propsA != propsB, false); if (verbose) { qDebug() << "AFTER propsB -= EXAMPLE_PROP_PAUSE_SIMULATION..."; } propsB -= EXAMPLE_PROP_PAUSE_SIMULATION; QCOMPARE(propsA == propsB, false); if (verbose) { qDebug() << "AFTER propsB = propsA..."; } propsB = propsA; QCOMPARE(propsA == propsB, true); } { if (verbose) { qDebug() << "Test 11: ExamplePropertyFlags testing individual properties"; } ExamplePropertyFlags props; if (verbose) { qDebug() << "ExamplePropertyFlags props;"; } QByteArray encoded = props.encode(); if (verbose) { qDebug() << "Test 11b: props.getHasProperty(EXAMPLE_PROP_VISIBLE)" << (props.getHasProperty(EXAMPLE_PROP_VISIBLE)) << "{ expect false }"; } QCOMPARE(props.getHasProperty(EXAMPLE_PROP_VISIBLE), false); if (verbose) { qDebug() << "props << EXAMPLE_PROP_VISIBLE;"; } props << EXAMPLE_PROP_VISIBLE; QCOMPARE(props.getHasProperty(EXAMPLE_PROP_VISIBLE), true); encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 16 })); if (verbose) { qDebug() << "props << EXAMPLE_PROP_ANIMATION_URL;"; } props << EXAMPLE_PROP_ANIMATION_URL; encoded = props.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 136, 16})); QCOMPARE(props.getHasProperty(EXAMPLE_PROP_VISIBLE), true); if (verbose) { qDebug() << "props << ... more ..."; } props << EXAMPLE_PROP_ANIMATION_FPS; props << EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props << EXAMPLE_PROP_ANIMATION_PLAYING; props << EXAMPLE_PROP_PAUSE_SIMULATION; encoded = props.encode(); QCOMPARE(props.getHasProperty(EXAMPLE_PROP_VISIBLE), true); if (verbose) { qDebug() << "ExamplePropertyFlags propsB = props & EXAMPLE_PROP_VISIBLE;"; } ExamplePropertyFlags propsB = props & EXAMPLE_PROP_VISIBLE; QCOMPARE(propsB.getHasProperty(EXAMPLE_PROP_VISIBLE), true); encoded = propsB.encode(); QCOMPARE(encoded, makeQByteArray({ (char) 16 })); if (verbose) { qDebug() << "ExamplePropertyFlags propsC = ~propsB;"; } ExamplePropertyFlags propsC = ~propsB; QCOMPARE(propsC.getHasProperty(EXAMPLE_PROP_VISIBLE), false); encoded = propsC.encode(); if (verbose) { qDebug() << "propsC... encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } } { if (verbose) { qDebug() << "Test 12: ExamplePropertyFlags: decode tests"; } ExamplePropertyFlags props; props << EXAMPLE_PROP_VISIBLE; props << EXAMPLE_PROP_ANIMATION_URL; props << EXAMPLE_PROP_ANIMATION_FPS; props << EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props << EXAMPLE_PROP_ANIMATION_PLAYING; props << EXAMPLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); qDebug() << "encoded.size()=" << encoded.size(); } ExamplePropertyFlags propsDecoded; propsDecoded.decode(encoded); QCOMPARE(propsDecoded, props); QByteArray encodedAfterDecoded = propsDecoded.encode(); QCOMPARE(encoded, encodedAfterDecoded); if (verbose) { qDebug() << "fill encoded byte array with extra garbage (as if it was bitstream with more content)"; } QByteArray extraContent; extraContent.fill(0xbaU, 10); encoded.append(extraContent); if (verbose) { qDebug() << "encoded.size()=" << encoded.size() << "includes extra garbage"; } ExamplePropertyFlags propsDecodedExtra; propsDecodedExtra.decode(encoded); QCOMPARE(propsDecodedExtra, props); QByteArray encodedAfterDecodedExtra = propsDecodedExtra.encode(); if (verbose) { qDebug() << "encodedAfterDecodedExtra="; outputBufferBits((const unsigned char*)encodedAfterDecodedExtra.constData(), encodedAfterDecodedExtra.size()); } } { if (verbose) { qDebug() << "Test 13: ExamplePropertyFlags: QByteArray << / >> tests"; } ExamplePropertyFlags props; props << EXAMPLE_PROP_VISIBLE; props << EXAMPLE_PROP_ANIMATION_URL; props << EXAMPLE_PROP_ANIMATION_FPS; props << EXAMPLE_PROP_ANIMATION_FRAME_INDEX; props << EXAMPLE_PROP_ANIMATION_PLAYING; props << EXAMPLE_PROP_PAUSE_SIMULATION; if (verbose) { qDebug() << "testing encoded << props"; } QByteArray encoded; encoded << props; if (verbose) { outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } ExamplePropertyFlags propsDecoded; if (verbose) { qDebug() << "testing encoded >> propsDecoded"; } encoded >> propsDecoded; QCOMPARE(propsDecoded, props); } if (verbose) { qDebug() << "******************************************************************************************"; } }
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { bool wantDebug = false; if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { // NOTE: This shouldn't happen. The only versions of the bit stream that didn't support split mtu buffers should // be handled by the model subclass and shouldn't call this routine. qDebug() << "EntityItem::readEntityDataFromBuffer()... " "ERROR CASE...args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU"; return 0; } // 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 (bytesLeftToRead >= MINIMUM_HEADER_BYTES) { int originalLength = bytesLeftToRead; QByteArray originalDataBuffer((const char*)data, originalLength); int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; const unsigned char* dataAt = data; // id QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size _id = QUuid::fromRfc4122(encodedID); _creatorTokenID = UNKNOWN_ENTITY_TOKEN; // if we know the id, then we don't care about the creator token _newlyCreated = false; dataAt += encodedID.size(); bytesRead += encodedID.size(); // type QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size ByteCountCoded<quint32> typeCoder = encodedType; encodedType = typeCoder; // determine true length dataAt += encodedType.size(); bytesRead += encodedType.size(); quint32 type = typeCoder; _type = (EntityTypes::EntityType)type; bool overwriteLocalData = true; // assume the new content overwrites our local data // _created quint64 createdFromBuffer = 0; memcpy(&createdFromBuffer, dataAt, sizeof(createdFromBuffer)); dataAt += sizeof(createdFromBuffer); bytesRead += sizeof(createdFromBuffer); createdFromBuffer -= clockSkew; _created = createdFromBuffer; // TODO: do we ever want to discard this??? if (wantDebug) { quint64 lastEdited = getLastEdited(); float editedAgo = getEditedAgo(); QString agoAsString = formatSecondsElapsed(editedAgo); QString ageAsString = formatSecondsElapsed(getAge()); qDebug() << "Loading entity " << getEntityItemID() << " from buffer..."; qDebug() << " _created =" << _created; qDebug() << " age=" << getAge() << "seconds - " << ageAsString; qDebug() << " lastEdited =" << lastEdited; qDebug() << " ago=" << editedAgo << "seconds - " << agoAsString; } quint64 now = usecTimestampNow(); quint64 lastEditedFromBuffer = 0; quint64 lastEditedFromBufferAdjusted = 0; // TODO: we could make this encoded as a delta from _created // _lastEdited memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer)); dataAt += sizeof(lastEditedFromBuffer); bytesRead += sizeof(lastEditedFromBuffer); lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew; bool fromSameServerEdit = (lastEditedFromBuffer == _lastEditedFromRemoteInRemoteTime); if (wantDebug) { qDebug() << "data from server **************** "; qDebug() << " entityItemID=" << getEntityItemID(); qDebug() << " now=" << now; qDebug() << " getLastEdited()=" << getLastEdited(); qDebug() << " lastEditedFromBuffer=" << lastEditedFromBuffer << " (BEFORE clockskew adjust)"; qDebug() << " clockSkew=" << clockSkew; qDebug() << " lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted << " (AFTER clockskew adjust)"; qDebug() << " _lastEditedFromRemote=" << _lastEditedFromRemote << " (our local time the last server edit we accepted)"; qDebug() << " _lastEditedFromRemoteInRemoteTime=" << _lastEditedFromRemoteInRemoteTime << " (remote time the last server edit we accepted)"; qDebug() << " fromSameServerEdit=" << fromSameServerEdit; } bool ignoreServerPacket = false; // assume we're use this server packet // If this packet is from the same server edit as the last packet we accepted from the server // we probably want to use it. if (fromSameServerEdit) { // If this is from the same sever packet, then check against any local changes since we got // the most recent packet from this server time if (_lastEdited > _lastEditedFromRemote) { ignoreServerPacket = true; } } else { // If this isn't from the same sever packet, then honor our skew adjusted times... // If we've changed our local tree more recently than the new data from this packet // then we will not be changing our values, instead we just read and skip the data if (_lastEdited > lastEditedFromBufferAdjusted) { ignoreServerPacket = true; } } if (ignoreServerPacket) { overwriteLocalData = false; if (wantDebug) { qDebug() << "IGNORING old data from server!!! ****************"; } } else { if (wantDebug) { qDebug() << "USING NEW data from server!!! ****************"; } _lastEdited = lastEditedFromBufferAdjusted; _lastEditedFromRemote = now; _lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer; somethingChangedNotification(); // notify derived classes that something has changed } // last updated is stored as ByteCountCoded delta from lastEdited QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta; quint64 updateDelta = updateDeltaCoder; if (overwriteLocalData) { _lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that for _lastEdited if (wantDebug) { qDebug() << "_lastUpdated=" << _lastUpdated; qDebug() << "_lastEdited=" << _lastEdited; qDebug() << "lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted; } } encodedUpdateDelta = updateDeltaCoder; // determine true length dataAt += encodedUpdateDelta.size(); bytesRead += encodedUpdateDelta.size(); // Property Flags QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size EntityPropertyFlags propertyFlags = encodedPropertyFlags; dataAt += propertyFlags.getEncodedLength(); bytesRead += propertyFlags.getEncodedLength(); READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, _position); // Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) { if (propertyFlags.getHasProperty(PROP_RADIUS)) { float fromBuffer; memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); dataAt += sizeof(fromBuffer); bytesRead += sizeof(fromBuffer); if (overwriteLocalData) { setRadius(fromBuffer); } if (wantDebug) { qDebug() << " readEntityDataFromBuffer() OLD FORMAT... found PROP_RADIUS"; } } } else { READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, _dimensions); if (wantDebug) { qDebug() << " readEntityDataFromBuffer() NEW FORMAT... look for PROP_DIMENSIONS"; } } if (wantDebug) { qDebug() << " readEntityDataFromBuffer() _dimensions:" << getDimensionsInMeters() << " in meters"; } READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation); READ_ENTITY_PROPERTY(PROP_MASS, float, _mass); READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity); READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, _gravity); READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping); READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime); READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript); READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint); READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity); READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible); READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, _ignoreForCollisions); READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, _collisionsWillMove); if (wantDebug) { qDebug() << " readEntityDataFromBuffer() _registrationPoint:" << _registrationPoint; qDebug() << " readEntityDataFromBuffer() _visible:" << _visible; qDebug() << " readEntityDataFromBuffer() _ignoreForCollisions:" << _ignoreForCollisions; qDebug() << " readEntityDataFromBuffer() _collisionsWillMove:" << _collisionsWillMove; } bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); recalculateCollisionShape(); } return bytesRead; }
void OctreeTests::propertyFlagsTests(bool verbose) { int testsTaken = 0; int testsPassed = 0; int testsFailed = 0; if (verbose) { qDebug() << "******************************************************************************************"; } qDebug() << "OctreeTests::propertyFlagsTests()"; { if (verbose) { qDebug() << "Test 1: EntityProperties: using setHasProperty()"; } testsTaken++; EntityPropertyFlags props; props.setHasProperty(PROP_VISIBLE); props.setHasProperty(PROP_POSITION); props.setHasProperty(PROP_RADIUS); props.setHasProperty(PROP_MODEL_URL); props.setHasProperty(PROP_ROTATION); QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { 31 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 1: EntityProperties: using setHasProperty()"; } } { if (verbose) { qDebug() << "Test 2: ParticlePropertyFlags: using setHasProperty()"; } testsTaken++; ParticlePropertyFlags props2; props2.setHasProperty(PARTICLE_PROP_VISIBLE); props2.setHasProperty(PARTICLE_PROP_ANIMATION_URL); props2.setHasProperty(PARTICLE_PROP_ANIMATION_FPS); props2.setHasProperty(PARTICLE_PROP_ANIMATION_FRAME_INDEX); props2.setHasProperty(PARTICLE_PROP_ANIMATION_PLAYING); props2.setHasProperty(PARTICLE_PROP_PAUSE_SIMULATION); QByteArray encoded = props2.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 2: ParticlePropertyFlags: using setHasProperty()"; } if (verbose) { qDebug() << "Test 2b: remove flag with setHasProperty() PARTICLE_PROP_PAUSE_SIMULATION"; } testsTaken++; props2.setHasProperty(PARTICLE_PROP_PAUSE_SIMULATION, false); encoded = props2.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytesB[] = { (char)136, (char)30 }; QByteArray expectedResultB(expectedBytesB, sizeof(expectedBytesB)/sizeof(expectedBytesB[0])); if (encoded == expectedResultB) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 2b: remove flag with setHasProperty() PARTICLE_PROP_PAUSE_SIMULATION"; } } { if (verbose) { qDebug() << "Test 3: ParticlePropertyFlags: using | operator"; } testsTaken++; ParticlePropertyFlags props; props = ParticlePropertyFlags(PARTICLE_PROP_VISIBLE) | ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_URL) | ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_FPS) | ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_FRAME_INDEX) | ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_PLAYING) | ParticlePropertyFlags(PARTICLE_PROP_PAUSE_SIMULATION); QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 3: ParticlePropertyFlags: using | operator"; } if (verbose) { qDebug() << "Test 3b: remove flag with -= PARTICLE_PROP_PAUSE_SIMULATION"; } testsTaken++; props -= PARTICLE_PROP_PAUSE_SIMULATION; encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytesB[] = { (char)136, (char)30 }; QByteArray expectedResultB(expectedBytesB, sizeof(expectedBytesB)/sizeof(expectedBytesB[0])); if (encoded == expectedResultB) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 3b: remove flag with -= PARTICLE_PROP_PAUSE_SIMULATION"; } } { if (verbose) { qDebug() << "Test 3c: ParticlePropertyFlags: using |= operator"; } testsTaken++; ParticlePropertyFlags props; props |= PARTICLE_PROP_VISIBLE; props |= PARTICLE_PROP_ANIMATION_URL; props |= PARTICLE_PROP_ANIMATION_FPS; props |= PARTICLE_PROP_ANIMATION_FRAME_INDEX; props |= PARTICLE_PROP_ANIMATION_PLAYING; props |= PARTICLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - 3c: ParticlePropertyFlags: using |= operator"; } } { if (verbose) { qDebug() << "Test 4: ParticlePropertyFlags: using + operator"; } testsTaken++; ParticlePropertyFlags props; props = ParticlePropertyFlags(PARTICLE_PROP_VISIBLE) + ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_URL) + ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_FPS) + ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_FRAME_INDEX) + ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_PLAYING) + ParticlePropertyFlags(PARTICLE_PROP_PAUSE_SIMULATION); QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 4: ParticlePropertyFlags: using + operator"; } } { if (verbose) { qDebug() << "Test 5: ParticlePropertyFlags: using += operator"; } testsTaken++; ParticlePropertyFlags props; props += PARTICLE_PROP_VISIBLE; props += PARTICLE_PROP_ANIMATION_URL; props += PARTICLE_PROP_ANIMATION_FPS; props += PARTICLE_PROP_ANIMATION_FRAME_INDEX; props += PARTICLE_PROP_ANIMATION_PLAYING; props += PARTICLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 5: ParticlePropertyFlags: using += operator"; } } { if (verbose) { qDebug() << "Test 6: ParticlePropertyFlags: using = ... << operator"; } testsTaken++; ParticlePropertyFlags props; props = ParticlePropertyFlags(PARTICLE_PROP_VISIBLE) << ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_URL) << ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_FPS) << ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_FRAME_INDEX) << ParticlePropertyFlags(PARTICLE_PROP_ANIMATION_PLAYING) << ParticlePropertyFlags(PARTICLE_PROP_PAUSE_SIMULATION); QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 6: ParticlePropertyFlags: using = ... << operator"; } } { if (verbose) { qDebug() << "Test 7: ParticlePropertyFlags: using <<= operator"; } testsTaken++; ParticlePropertyFlags props; props <<= PARTICLE_PROP_VISIBLE; props <<= PARTICLE_PROP_ANIMATION_URL; props <<= PARTICLE_PROP_ANIMATION_FPS; props <<= PARTICLE_PROP_ANIMATION_FRAME_INDEX; props <<= PARTICLE_PROP_ANIMATION_PLAYING; props <<= PARTICLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 7: ParticlePropertyFlags: using <<= operator"; } } { if (verbose) { qDebug() << "Test 8: ParticlePropertyFlags: using << enum operator"; } testsTaken++; ParticlePropertyFlags props; props << PARTICLE_PROP_VISIBLE; props << PARTICLE_PROP_ANIMATION_URL; props << PARTICLE_PROP_ANIMATION_FPS; props << PARTICLE_PROP_ANIMATION_FRAME_INDEX; props << PARTICLE_PROP_ANIMATION_PLAYING; props << PARTICLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 8: ParticlePropertyFlags: using << enum operator"; } } { if (verbose) { qDebug() << "Test 9: ParticlePropertyFlags: using << flags operator "; } testsTaken++; ParticlePropertyFlags props; ParticlePropertyFlags props2; props << PARTICLE_PROP_VISIBLE; props << PARTICLE_PROP_ANIMATION_URL; props << PARTICLE_PROP_ANIMATION_FPS; props2 << PARTICLE_PROP_ANIMATION_FRAME_INDEX; props2 << PARTICLE_PROP_ANIMATION_PLAYING; props2 << PARTICLE_PROP_PAUSE_SIMULATION; props << props2; QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { (char)196, (char)15, (char)2 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 9: ParticlePropertyFlags: using << flags operator"; } } { if (verbose) { qDebug() << "Test 10: ParticlePropertyFlags comparison"; } ParticlePropertyFlags propsA; if (verbose) { qDebug() << "!propsA:" << (!propsA) << "{ expect true }"; } testsTaken++; bool resultA = (!propsA); bool expectedA = true; if (resultA == expectedA) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 10a: ParticlePropertyFlags comparison, uninitialized !propsA"; } propsA << PARTICLE_PROP_VISIBLE; propsA << PARTICLE_PROP_ANIMATION_URL; propsA << PARTICLE_PROP_ANIMATION_FPS; propsA << PARTICLE_PROP_ANIMATION_FRAME_INDEX; propsA << PARTICLE_PROP_ANIMATION_PLAYING; propsA << PARTICLE_PROP_PAUSE_SIMULATION; if (verbose) { qDebug() << "!propsA:" << (!propsA) << "{ expect false }"; } testsTaken++; bool resultB = (!propsA); bool expectedB = false; if (resultB == expectedB) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 10b: ParticlePropertyFlags comparison, initialized !propsA"; } ParticlePropertyFlags propsB; propsB << PARTICLE_PROP_VISIBLE; propsB << PARTICLE_PROP_ANIMATION_URL; propsB << PARTICLE_PROP_ANIMATION_FPS; propsB << PARTICLE_PROP_ANIMATION_FRAME_INDEX; propsB << PARTICLE_PROP_ANIMATION_PLAYING; propsB << PARTICLE_PROP_PAUSE_SIMULATION; if (verbose) { qDebug() << "propsA == propsB:" << (propsA == propsB) << "{ expect true }"; qDebug() << "propsA != propsB:" << (propsA != propsB) << "{ expect false }"; } testsTaken++; bool resultC = (propsA == propsB); bool expectedC = true; if (resultC == expectedC) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 10c: ParticlePropertyFlags comparison, propsA == propsB"; } testsTaken++; bool resultD = (propsA != propsB); bool expectedD = false; if (resultD == expectedD) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 10d: ParticlePropertyFlags comparison, propsA != propsB"; } if (verbose) { qDebug() << "AFTER propsB -= PARTICLE_PROP_PAUSE_SIMULATION..."; } propsB -= PARTICLE_PROP_PAUSE_SIMULATION; if (verbose) { qDebug() << "propsA == propsB:" << (propsA == propsB) << "{ expect false }"; qDebug() << "propsA != propsB:" << (propsA != propsB) << "{ expect true }"; } testsTaken++; bool resultE = (propsA == propsB); bool expectedE = false; if (resultE == expectedE) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 10e: ParticlePropertyFlags comparison, AFTER propsB -= PARTICLE_PROP_PAUSE_SIMULATION"; } if (verbose) { qDebug() << "AFTER propsB = propsA..."; } propsB = propsA; if (verbose) { qDebug() << "propsA == propsB:" << (propsA == propsB) << "{ expect true }"; qDebug() << "propsA != propsB:" << (propsA != propsB) << "{ expect false }"; } testsTaken++; bool resultF = (propsA == propsB); bool expectedF = true; if (resultF == expectedF) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 10f: ParticlePropertyFlags comparison, AFTER propsB = propsA"; } } { if (verbose) { qDebug() << "Test 11: ParticlePropertyFlags testing individual properties"; } ParticlePropertyFlags props; if (verbose) { qDebug() << "ParticlePropertyFlags props;"; } QByteArray encoded = props.encode(); if (verbose) { qDebug() << "props... encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytes[] = { 0 }; QByteArray expectedResult(expectedBytes, sizeof(expectedBytes)/sizeof(expectedBytes[0])); testsTaken++; if (encoded == expectedResult) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11a: ParticlePropertyFlags testing individual properties"; } if (verbose) { qDebug() << "Test 11b: props.getHasProperty(PARTICLE_PROP_VISIBLE)" << (props.getHasProperty(PARTICLE_PROP_VISIBLE)) << "{ expect false }"; } testsTaken++; bool resultB = props.getHasProperty(PARTICLE_PROP_VISIBLE); bool expectedB = false; if (resultB == expectedB) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11b: props.getHasProperty(PARTICLE_PROP_VISIBLE)"; } if (verbose) { qDebug() << "props << PARTICLE_PROP_VISIBLE;"; } props << PARTICLE_PROP_VISIBLE; testsTaken++; bool resultC = props.getHasProperty(PARTICLE_PROP_VISIBLE); bool expectedC = true; if (resultC == expectedC) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11c: props.getHasProperty(PARTICLE_PROP_VISIBLE) after props << PARTICLE_PROP_VISIBLE"; } encoded = props.encode(); if (verbose) { qDebug() << "props... encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); qDebug() << "props.getHasProperty(PARTICLE_PROP_VISIBLE)" << (props.getHasProperty(PARTICLE_PROP_VISIBLE)) << "{ expect true }"; } char expectedBytesC[] = { 16 }; QByteArray expectedResultC(expectedBytesC, sizeof(expectedBytesC)/sizeof(expectedBytesC[0])); testsTaken++; if (encoded == expectedResultC) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11c: ParticlePropertyFlags testing individual properties"; } if (verbose) { qDebug() << "props << PARTICLE_PROP_ANIMATION_URL;"; } props << PARTICLE_PROP_ANIMATION_URL; encoded = props.encode(); if (verbose) { qDebug() << "props... encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); qDebug() << "props.getHasProperty(PARTICLE_PROP_VISIBLE)" << (props.getHasProperty(PARTICLE_PROP_VISIBLE)) << "{ expect true }"; } char expectedBytesD[] = { (char)136, (char)16 }; QByteArray expectedResultD(expectedBytesD, sizeof(expectedBytesD)/sizeof(expectedBytesD[0])); testsTaken++; if (encoded == expectedResultD) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11d: ParticlePropertyFlags testing individual properties"; } testsTaken++; bool resultE = props.getHasProperty(PARTICLE_PROP_VISIBLE); bool expectedE = true; if (resultE == expectedE) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11e: props.getHasProperty(PARTICLE_PROP_VISIBLE) after props << PARTICLE_PROP_ANIMATION_URL"; } if (verbose) { qDebug() << "props << ... more ..."; } props << PARTICLE_PROP_ANIMATION_FPS; props << PARTICLE_PROP_ANIMATION_FRAME_INDEX; props << PARTICLE_PROP_ANIMATION_PLAYING; props << PARTICLE_PROP_PAUSE_SIMULATION; encoded = props.encode(); if (verbose) { qDebug() << "props... encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); qDebug() << "props.getHasProperty(PARTICLE_PROP_VISIBLE)" << (props.getHasProperty(PARTICLE_PROP_VISIBLE)) << "{ expect true }"; } testsTaken++; bool resultF = props.getHasProperty(PARTICLE_PROP_VISIBLE); bool expectedF = true; if (resultF == expectedF) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11f: props.getHasProperty(PARTICLE_PROP_VISIBLE) after props << more"; } if (verbose) { qDebug() << "ParticlePropertyFlags propsB = props & PARTICLE_PROP_VISIBLE;"; } ParticlePropertyFlags propsB = props & PARTICLE_PROP_VISIBLE; if (verbose) { qDebug() << "propsB.getHasProperty(PARTICLE_PROP_VISIBLE)" << (propsB.getHasProperty(PARTICLE_PROP_VISIBLE)) << "{ expect true }"; } testsTaken++; bool resultG = propsB.getHasProperty(PARTICLE_PROP_VISIBLE); bool expectedG = true; if (resultG == expectedG) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11g: propsB = props & PARTICLE_PROP_VISIBLE"; } encoded = propsB.encode(); if (verbose) { qDebug() << "propsB... encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } char expectedBytesH[] = { 16 }; QByteArray expectedResultH(expectedBytesC, sizeof(expectedBytesH)/sizeof(expectedBytesH[0])); testsTaken++; if (encoded == expectedResultH) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11h: ParticlePropertyFlags testing individual properties"; } if (verbose) { qDebug() << "ParticlePropertyFlags propsC = ~propsB;"; } ParticlePropertyFlags propsC = ~propsB; if (verbose) { qDebug() << "propsC.getHasProperty(PARTICLE_PROP_VISIBLE)" << (propsC.getHasProperty(PARTICLE_PROP_VISIBLE)) << "{ expect false }"; } testsTaken++; bool resultI = propsC.getHasProperty(PARTICLE_PROP_VISIBLE); bool expectedI = false; if (resultI == expectedI) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 11i: propsC = ~propsB"; } encoded = propsC.encode(); if (verbose) { qDebug() << "propsC... encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } } { if (verbose) { qDebug() << "Test 12: ParticlePropertyFlags: decode tests"; } ParticlePropertyFlags props; props << PARTICLE_PROP_VISIBLE; props << PARTICLE_PROP_ANIMATION_URL; props << PARTICLE_PROP_ANIMATION_FPS; props << PARTICLE_PROP_ANIMATION_FRAME_INDEX; props << PARTICLE_PROP_ANIMATION_PLAYING; props << PARTICLE_PROP_PAUSE_SIMULATION; QByteArray encoded = props.encode(); if (verbose) { qDebug() << "encoded="; outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); qDebug() << "encoded.size()=" << encoded.size(); } ParticlePropertyFlags propsDecoded; propsDecoded.decode(encoded); if (verbose) { qDebug() << "propsDecoded == props:" << (propsDecoded == props) << "{ expect true }"; } testsTaken++; bool resultA = (propsDecoded == props); bool expectedA = true; if (resultA == expectedA) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 12a: propsDecoded == props"; } QByteArray encodedAfterDecoded = propsDecoded.encode(); if (verbose) { qDebug() << "encodedAfterDecoded="; outputBufferBits((const unsigned char*)encodedAfterDecoded.constData(), encodedAfterDecoded.size()); } testsTaken++; bool resultB = (encoded == encodedAfterDecoded); bool expectedB = true; if (resultB == expectedB) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 12b: (encoded == encodedAfterDecoded)"; } if (verbose) { qDebug() << "fill encoded byte array with extra garbage (as if it was bitstream with more content)"; } QByteArray extraContent; extraContent.fill(0xba, 10); encoded.append(extraContent); if (verbose) { qDebug() << "encoded.size()=" << encoded.size() << "includes extra garbage"; } ParticlePropertyFlags propsDecodedExtra; propsDecodedExtra.decode(encoded); if (verbose) { qDebug() << "propsDecodedExtra == props:" << (propsDecodedExtra == props) << "{ expect true }"; } testsTaken++; bool resultC = (propsDecodedExtra == props); bool expectedC = true; if (resultC == expectedC) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 12c: (propsDecodedExtra == props)"; } QByteArray encodedAfterDecodedExtra = propsDecodedExtra.encode(); if (verbose) { qDebug() << "encodedAfterDecodedExtra="; outputBufferBits((const unsigned char*)encodedAfterDecodedExtra.constData(), encodedAfterDecodedExtra.size()); } } { if (verbose) { qDebug() << "Test 13: ParticlePropertyFlags: QByteArray << / >> tests"; } ParticlePropertyFlags props; props << PARTICLE_PROP_VISIBLE; props << PARTICLE_PROP_ANIMATION_URL; props << PARTICLE_PROP_ANIMATION_FPS; props << PARTICLE_PROP_ANIMATION_FRAME_INDEX; props << PARTICLE_PROP_ANIMATION_PLAYING; props << PARTICLE_PROP_PAUSE_SIMULATION; if (verbose) { qDebug() << "testing encoded << props"; } QByteArray encoded; encoded << props; if (verbose) { outputBufferBits((const unsigned char*)encoded.constData(), encoded.size()); } ParticlePropertyFlags propsDecoded; if (verbose) { qDebug() << "testing encoded >> propsDecoded"; } encoded >> propsDecoded; if (verbose) { qDebug() << "propsDecoded==props" << (propsDecoded==props); } testsTaken++; bool resultA = (propsDecoded == props); bool expectedA = true; if (resultA == expectedA) { testsPassed++; } else { testsFailed++; qDebug() << "FAILED - Test 13: ParticlePropertyFlags: QByteArray << / >> tests"; } } qDebug() << " tests passed:" << testsPassed << "out of" << testsTaken; if (verbose) { qDebug() << "******************************************************************************************"; } }
// 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; }