diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 07788fefe0..7a5703a881 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -19,11 +19,23 @@ #include #include +#include int main(int argc, const char * argv[]) { timeval startup_time; gettimeofday(&startup_time, NULL); + // Debug option to demonstrate that the client's local time does not + // need to be in sync with any other network node. This forces clock + // skew for the individual client + const char* CLOCK_SKEW = "--clockSkew"; + const char* clockSkewOption = getCmdOption(argc, argv, CLOCK_SKEW); + if (clockSkewOption) { + int clockSkew = atoi(clockSkewOption); + usecTimestampNowForceClockSkew(clockSkew); + qDebug("clockSkewOption=%s clockSkew=%d\n", clockSkewOption, clockSkew); + } + int exitCode; { Application app(argc, const_cast(argv), startup_time); diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index ffb5f9c976..a8010cefaf 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -656,6 +656,17 @@ void OctreeServer::run() { _persistThread->initialize(true); } } + + // Debug option to demonstrate that the server's local time does not + // need to be in sync with any other network node. This forces clock + // skew for the individual server node + const char* CLOCK_SKEW = "--clockSkew"; + const char* clockSkewOption = getCmdOption(_argc, _argv, CLOCK_SKEW); + if (clockSkewOption) { + int clockSkew = atoi(clockSkewOption); + usecTimestampNowForceClockSkew(clockSkew); + qDebug("clockSkewOption=%s clockSkew=%d\n", clockSkewOption, clockSkew); + } // Check to see if the user passed in a command line option for setting packet send rate const char* PACKETS_PER_SECOND = "--packetsPerSecond"; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index fb5d603d7d..9248bf540f 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -36,15 +36,16 @@ Particle::~Particle() { void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript, uint32_t id) { if (id == NEW_PARTICLE) { - _created = usecTimestampNow(); _id = _nextID; _nextID++; } else { _id = id; } - _lastUpdated = usecTimestampNow(); - _lastEdited = _lastUpdated; - + uint64_t now = usecTimestampNow(); + _edited = now; + _lastSimulated = now; + _created = now; // will get updated as appropriate in setLifetime() + _position = position; _radius = radius; memcpy(_color, color, sizeof(_color)); @@ -63,13 +64,10 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const { //printf("Particle::appendParticleData()... getID()=%d\n", getID()); if (success) { - success = packetData->appendValue(getCreated()); + success = packetData->appendValue(getLifetime()); } if (success) { - success = packetData->appendValue(getLastUpdated()); - } - if (success) { - success = packetData->appendValue(getLastEdited()); + success = packetData->appendValue(getEditedAgo()); } if (success) { success = packetData->appendValue(getRadius()); @@ -103,9 +101,17 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const { } int Particle::expectedBytes() { - int expectedBytes = sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(float) + - sizeof(glm::vec3) + sizeof(rgbColor) + sizeof(glm::vec3) + - sizeof(glm::vec3) + sizeof(float) + sizeof(bool); + int expectedBytes = sizeof(uint32_t) // id + + sizeof(float) // lifetime + + sizeof(float) // edited ago + + sizeof(float) // radius + + sizeof(glm::vec3) // position + + sizeof(rgbColor) // color + + sizeof(glm::vec3) // velocity + + sizeof(glm::vec3) // gravity + + sizeof(float) // damping + + sizeof(bool); // inhand + // potentially more... return expectedBytes; } @@ -119,20 +125,19 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef dataAt += sizeof(_id); bytesRead += sizeof(_id); - // created - memcpy(&_created, dataAt, sizeof(_created)); - dataAt += sizeof(_created); - bytesRead += sizeof(_created); + // lifetime + float lifetime; + memcpy(&lifetime, dataAt, sizeof(lifetime)); + dataAt += sizeof(lifetime); + bytesRead += sizeof(lifetime); + setLifetime(lifetime); - // lastupdated - memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated)); - dataAt += sizeof(_lastUpdated); - bytesRead += sizeof(_lastUpdated); - - // _lastEdited - memcpy(&_lastEdited, dataAt, sizeof(_lastEdited)); - dataAt += sizeof(_lastEdited); - bytesRead += sizeof(_lastEdited); + // edited ago + float editedAgo; + memcpy(&editedAgo, dataAt, sizeof(editedAgo)); + dataAt += sizeof(editedAgo); + bytesRead += sizeof(editedAgo); + setEditedAgo(editedAgo); // radius memcpy(&_radius, dataAt, sizeof(_radius)); @@ -186,7 +191,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes) { - Particle newParticle; // id and lastUpdated will get set here... + Particle newParticle; // id and _lastSimulated will get set here... unsigned char* dataAt = data; processedBytes = 0; @@ -214,26 +219,17 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe processedBytes += sizeof(creatorTokenID); newParticle.setCreatorTokenID(creatorTokenID); newParticle._newlyCreated = true; + + newParticle.setLifetime(0); // this guy is new! + } else { newParticle._id = editID; newParticle._newlyCreated = false; } - // created - memcpy(&newParticle._created, dataAt, sizeof(newParticle._created)); - dataAt += sizeof(newParticle._created); - processedBytes += sizeof(newParticle._created); - - // lastUpdated - memcpy(&newParticle._lastUpdated, dataAt, sizeof(newParticle._lastUpdated)); - dataAt += sizeof(newParticle._lastUpdated); - processedBytes += sizeof(newParticle._lastUpdated); - - // lastEdited - memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited)); - dataAt += sizeof(newParticle._lastEdited); - processedBytes += sizeof(newParticle._lastEdited); - + // clearly we just edited it + newParticle.setEditedAgo(0); + // radius memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius)); dataAt += sizeof(newParticle._radius); @@ -291,9 +287,8 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe void Particle::debugDump() const { printf("Particle id :%u\n", _id); - printf(" created:%llu\n", _created); - printf(" last updated:%llu\n", _lastUpdated); - printf(" last edited:%llu\n", _lastEdited); + printf(" lifetime:%f\n", getLifetime()); + printf(" edited ago:%f\n", getEditedAgo()); printf(" position:%f,%f,%f\n", _position.x, _position.y, _position.z); printf(" velocity:%f,%f,%f\n", _velocity.x, _velocity.y, _velocity.z); printf(" gravity:%f,%f,%f\n", _gravity.x, _gravity.y, _gravity.z); @@ -325,7 +320,6 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, sizeOut += lengthOfOctcode; // Now add our edit content details... - uint64_t created = usecTimestampNow(); // id memcpy(copyAt, &details[i].id, sizeof(details[i].id)); @@ -338,25 +332,8 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, memcpy(copyAt, &details[i].creatorTokenID, sizeof(details[i].creatorTokenID)); copyAt += sizeof(details[i].creatorTokenID); sizeOut += sizeof(details[i].creatorTokenID); - } else { - created = 0; } - // created - memcpy(copyAt, &created, sizeof(created)); - copyAt += sizeof(created); - sizeOut += sizeof(created); - - // lastUpdated - memcpy(copyAt, &details[i].lastUpdated, sizeof(details[i].lastUpdated)); - copyAt += sizeof(details[i].lastUpdated); - sizeOut += sizeof(details[i].lastUpdated); - - // lastEdited - memcpy(copyAt, &details[i].lastEdited, sizeof(details[i].lastEdited)); - copyAt += sizeof(details[i].lastEdited); - sizeOut += sizeof(details[i].lastEdited); - // radius memcpy(copyAt, &details[i].radius, sizeof(details[i].radius)); copyAt += sizeof(details[i].radius); @@ -405,7 +382,6 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, if (wantDebugging) { printf("encodeParticleEditMessageDetails()....\n"); printf("Particle id :%u\n", details[i].id); - printf(" last updated:%llu\n", details[i].lastUpdated); printf(" nextID:%u\n", _nextID); } } @@ -418,21 +394,30 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, void Particle::update() { + uint64_t now = usecTimestampNow(); - uint64_t elapsed = now - _lastUpdated; - uint64_t USECS_PER_SECOND = 1000 * 1000; + uint64_t elapsed = now - _lastSimulated; float timeElapsed = (float)((float)elapsed/(float)USECS_PER_SECOND); + // calculate our default shouldDie state... then allow script to change it if it wants... float velocityScalar = glm::length(getVelocity()); const float STILL_MOVING = 0.05 / TREE_SCALE; bool isStillMoving = (velocityScalar > STILL_MOVING); - const uint64_t REALLY_OLD = 30 * 1000 * 1000; + const float REALLY_OLD = 30.0f; // 30 seconds bool isReallyOld = (getLifetime() > REALLY_OLD); bool isInHand = getInHand(); bool shouldDie = !isInHand && !isStillMoving && isReallyOld; setShouldDie(shouldDie); - + + bool wantDebug = false; + if (wantDebug) { + printf("Particle::update()... timeElapsed: %f lifeTime:%f editedAgo:%f " + "isInHand:%s isStillMoveing:%s isReallyOld:%s shouldDie:%s\n", + timeElapsed, getLifetime(), getEditedAgo(), debug::valueOf(isInHand), debug::valueOf(isStillMoving), + debug::valueOf(isReallyOld), debug::valueOf(shouldDie)); + } + runScript(); // allow the javascript to alter our state // If the ball is in hand, it doesn't move or have gravity effect it @@ -454,7 +439,7 @@ void Particle::update() { //printf("applying damping to Particle timeElapsed=%f\n",timeElapsed); } - _lastUpdated = now; + _lastSimulated = now; } void Particle::runScript() { @@ -484,3 +469,19 @@ void Particle::runScript() { } } } + +void Particle::setLifetime(float lifetime) { + uint64_t lifetimeInUsecs = lifetime * USECS_PER_SECOND; + _created = usecTimestampNow() - lifetimeInUsecs; +} + +void Particle::setEditedAgo(float editedAgo) { + uint64_t editedAgoInUsecs = editedAgo * USECS_PER_SECOND; + _edited = usecTimestampNow() - editedAgoInUsecs; +} + +void Particle::copyChangedProperties(const Particle& other) { + float lifetime = getLifetime(); + *this = other; + setLifetime(lifetime); +} diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 33e8960670..995a8675ce 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -25,8 +25,6 @@ const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF; class ParticleDetail { public: uint32_t id; - uint64_t lastUpdated; - uint64_t lastEdited; glm::vec3 position; float radius; rgbColor color; @@ -68,10 +66,11 @@ public: const glm::vec3& getGravity() const { return _gravity; } bool getInHand() const { return _inHand; } float getDamping() const { return _damping; } - uint64_t getCreated() const { return _created; } - uint64_t getLifetime() const { return usecTimestampNow() - _created; } - uint64_t getLastUpdated() const { return _lastUpdated; } - uint64_t getLastEdited() const { return _lastEdited; } + + /// lifetime of the particle in seconds + float getLifetime() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } + /// seconds since last edited + float getEditedAgo() const { return (float)(usecTimestampNow() - _edited) / (float)USECS_PER_SECOND; } uint32_t getID() const { return _id; } bool getShouldDie() const { return _shouldDie; } QString getUpdateScript() const { return _updateScript; } @@ -93,7 +92,6 @@ public: void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; } void setUpdateScript(QString updateScript) { _updateScript = updateScript; } void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; } - void setCreated(uint64_t created) { _created = created; } bool appendParticleData(OctreePacketData* packetData) const; int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); @@ -105,20 +103,24 @@ public: void update(); void debugDump() const; + + // similar to assignment/copy, but it handles keeping lifetime accurate + void copyChangedProperties(const Particle& other); + protected: void runScript(); static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3); static void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color); static void xColorFromScriptValue(const QScriptValue &object, xColor& color); + + void setLifetime(float lifetime); + void setEditedAgo(float editedAgo); glm::vec3 _position; rgbColor _color; float _radius; glm::vec3 _velocity; - uint64_t _lastUpdated; - uint64_t _created; - uint64_t _lastEdited; uint32_t _id; static uint32_t _nextID; bool _shouldDie; @@ -129,6 +131,12 @@ protected: uint32_t _creatorTokenID; bool _newlyCreated; + + // these are never included in wire time + uint64_t _lastSimulated; + uint64_t _created; + uint64_t _edited; + }; class ParticleScriptObject : public QObject { @@ -144,9 +152,7 @@ public slots: float getDamping() const { return _particle->getDamping(); } float getRadius() const { return _particle->getRadius(); } bool getShouldDie() { return _particle->getShouldDie(); } - float getCreated() const { return ((float)_particle->getCreated() / (float)USECS_PER_SECOND); } - float getLifetime() const { return ((float)_particle->getLifetime() / (float)USECS_PER_SECOND); } - + float getLifetime() const { return _particle->getLifetime(); } void setPosition(glm::vec3 value) { _particle->setPosition(value); } void setVelocity(glm::vec3 value) { _particle->setVelocity(value); } diff --git a/libraries/particles/src/ParticleEditHandle.cpp b/libraries/particles/src/ParticleEditHandle.cpp index 28356ce63e..d8466816c4 100644 --- a/libraries/particles/src/ParticleEditHandle.cpp +++ b/libraries/particles/src/ParticleEditHandle.cpp @@ -44,8 +44,7 @@ void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor glm::vec3 gravity, float damping, bool inHand, QString updateScript) { // setup a ParticleDetail struct with the data - uint64_t now = usecTimestampNow(); - ParticleDetail addParticleDetail = { NEW_PARTICLE, now, now, + ParticleDetail addParticleDetail = { NEW_PARTICLE, position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, inHand, updateScript, _creatorTokenID }; @@ -70,8 +69,7 @@ bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor } // setup a ParticleDetail struct with the data - uint64_t now = usecTimestampNow(); - ParticleDetail newParticleDetail = { _id, now, now, + ParticleDetail newParticleDetail = { _id, position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, inHand, updateScript, _creatorTokenID }; diff --git a/libraries/particles/src/ParticleScriptingInterface.cpp b/libraries/particles/src/ParticleScriptingInterface.cpp index ec8209208c..758b50035d 100644 --- a/libraries/particles/src/ParticleScriptingInterface.cpp +++ b/libraries/particles/src/ParticleScriptingInterface.cpp @@ -22,8 +22,7 @@ unsigned int ParticleScriptingInterface::queueParticleAdd(glm::vec3 position, fl _nextCreatorTokenID++; // setup a ParticleDetail struct with the data - uint64_t now = usecTimestampNow(); - ParticleDetail addParticleDetail = { NEW_PARTICLE, now, now, + ParticleDetail addParticleDetail = { NEW_PARTICLE, position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, inHand, updateScript, creatorTokenID }; diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index a631eae926..b142c1f568 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -119,33 +119,19 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) { uint16_t numberOfParticles = _particles.size(); for (uint16_t i = 0; i < numberOfParticles; i++) { if (_particles[i].getID() == particle.getID()) { - int difference = _particles[i].getLastUpdated() - particle.getLastUpdated(); - - bool changedOnServer = _particles[i].getLastEdited() < particle.getLastEdited(); - bool localOlder = _particles[i].getLastUpdated() < particle.getLastUpdated(); - - if (changedOnServer || localOlder) { - + bool changedOnServer = _particles[i].getEditedAgo() > particle.getEditedAgo(); + if (changedOnServer) { if (wantDebug) { - printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n", + printf("local particle [id:%d] %s, particle.isNewlyCreated()=%s\n", particle.getID(), (changedOnServer ? "CHANGED" : "same"), - (localOlder ? "OLDER" : "NEWER"), - difference, debug::valueOf(particle.isNewlyCreated()) ); + debug::valueOf(particle.isNewlyCreated()) ); } - - uint64_t actuallyCreated = particle.getCreated(); - if (!particle.isNewlyCreated()) { - actuallyCreated = _particles[i].getCreated(); - } - _particles[i] = particle; - _particles[i].setCreated(actuallyCreated); + _particles[i].copyChangedProperties(particle); } else { if (wantDebug) { - printf(">>> NO CHANGE <<< -- local particle [id:%d] %s and %s than server particle by %d, " - "particle.isNewlyCreated()=%s\n", + printf(">>> NO CHANGE <<< -- local particle [id:%d] %s particle.isNewlyCreated()=%s\n", particle.getID(), (changedOnServer ? "CHANGED" : "same"), - (localOlder ? "OLDER" : "NEWER"), - difference, debug::valueOf(particle.isNewlyCreated()) ); + debug::valueOf(particle.isNewlyCreated()) ); } } return true; diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index e7c16367aa..24865fff5c 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -54,7 +54,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { return 2; case PACKET_TYPE_PARTICLE_DATA: - return 3; + return 4; default: return 0; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 9a6bb8b4c3..4d178a6ee9 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -30,10 +30,15 @@ uint64_t usecTimestamp(const timeval *time) { return (time->tv_sec * 1000000 + time->tv_usec); } +int usecTimestampNowAdjust = 0; +void usecTimestampNowForceClockSkew(int clockSkew) { + ::usecTimestampNowAdjust = clockSkew; +} + uint64_t usecTimestampNow() { timeval now; gettimeofday(&now, NULL); - return (now.tv_sec * 1000000 + now.tv_usec); + return (now.tv_sec * 1000000 + now.tv_usec) + ::usecTimestampNowAdjust; } float randFloat () { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index cd6444624f..893922ec28 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -56,6 +56,7 @@ static const uint64_t USECS_PER_SECOND = 1000 * 1000; uint64_t usecTimestamp(const timeval *time); uint64_t usecTimestampNow(); +void usecTimestampNowForceClockSkew(int clockSkew); float randFloat(); int randIntInRange (int min, int max);