diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 97cd8a5c08..7375bd2149 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1251,12 +1251,10 @@ void Application::sendPingPackets() {
const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER,
NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER};
- uint64_t currentTime = usecTimestampNow();
- unsigned char pingPacket[numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_PING) + sizeof(currentTime)];
- int numHeaderBytes = populateTypeAndVersion(pingPacket, PACKET_TYPE_PING);
-
- memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime));
- getInstance()->controlledBroadcastToNodes(pingPacket, sizeof(pingPacket),
+ unsigned char pingPacket[MAX_PACKET_SIZE];
+ int length = NodeList::getInstance()->fillPingPacket(pingPacket);
+
+ getInstance()->controlledBroadcastToNodes(pingPacket, length,
nodesToPing, sizeof(nodesToPing));
}
@@ -4239,15 +4237,15 @@ void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t m
const HifiSockAddr& senderSockAddr, bool wasStatsPacket) {
// Attempt to identify the sender from it's address.
- Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
- if (voxelServer) {
- QUuid nodeUUID = voxelServer->getUUID();
+ Node* serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
+ if (serverNode) {
+ QUuid nodeUUID = serverNode->getUUID();
// now that we know the node ID, let's add these stats to the stats for that node...
_voxelSceneStatsLock.lockForWrite();
if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) {
VoxelSceneStats& stats = _octreeServerSceneStats[nodeUUID];
- stats.trackIncomingOctreePacket(messageData, messageLength, wasStatsPacket);
+ stats.trackIncomingOctreePacket(messageData, messageLength, wasStatsPacket, serverNode->getClockSkewUsec());
}
_voxelSceneStatsLock.unlock();
}
diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp
index 9e0eecb2e0..2b78ff73d6 100644
--- a/interface/src/VoxelPacketProcessor.cpp
+++ b/interface/src/VoxelPacketProcessor.cpp
@@ -56,12 +56,12 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket);
- Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
- if (voxelServer && *voxelServer->getActiveSocket() == senderSockAddr) {
+ Node* serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
+ if (serverNode && serverNode->getActiveSocket() && *serverNode->getActiveSocket() == senderSockAddr) {
switch(packetData[0]) {
case PACKET_TYPE_PARTICLE_DATA: {
- app->_particles.processDatagram(QByteArray((char*) packetData, messageLength), senderSockAddr);
+ app->_particles.processDatagram(QByteArray((char*) packetData, messageLength), senderSockAddr, serverNode);
} break;
case PACKET_TYPE_ENVIRONMENT_DATA: {
@@ -69,7 +69,7 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
} break;
default : {
- app->_voxels.setDataSourceUUID(voxelServer->getUUID());
+ app->_voxels.setDataSourceUUID(serverNode->getUUID());
app->_voxels.parseData(packetData, messageLength);
app->_voxels.setDataSourceUUID(QUuid());
} break;
diff --git a/interface/src/ui/VoxelStatsDialog.cpp b/interface/src/ui/VoxelStatsDialog.cpp
index 61226cd99a..3a2c8468ad 100644
--- a/interface/src/ui/VoxelStatsDialog.cpp
+++ b/interface/src/ui/VoxelStatsDialog.cpp
@@ -34,16 +34,16 @@ VoxelStatsDialog::VoxelStatsDialog(QWidget* parent, NodeToVoxelSceneStats* model
_labels[i] = NULL;
}
- this->setWindowTitle("Voxel Statistics");
+ this->setWindowTitle("Octree Server Statistics");
// Create layouter
_form = new QFormLayout();
this->QDialog::setLayout(_form);
// Setup stat items
- _serverVoxels = AddStatItem("Voxels on Servers");
- _localVoxels = AddStatItem("Local Voxels");
- _localVoxelsMemory = AddStatItem("Voxels Memory");
+ _serverVoxels = AddStatItem("Elements on Servers");
+ _localVoxels = AddStatItem("Local Elements");
+ _localVoxelsMemory = AddStatItem("Elements Memory");
_voxelsRendered = AddStatItem("Voxels Rendered");
_sendingMode = AddStatItem("Sending Mode");
@@ -136,7 +136,7 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
label = _labels[_localVoxelsMemory];
statsValue.str("");
statsValue <<
- "Nodes RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.f << "MB "
+ "Elements RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.f << "MB "
"Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB " <<
"VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB ";
if (voxels->hasVoxelMemoryUsageGPU()) {
@@ -344,15 +344,25 @@ void VoxelStatsDialog::showOctreeServersOfType(int& serverCount, NODE_TYPE serve
QString incomingWastedBytesString = locale.toString((uint)stats.getIncomingWastedBytes());
QString incomingOutOfOrderString = locale.toString((uint)stats.getIncomingOutOfOrder());
QString incomingLikelyLostString = locale.toString((uint)stats.getIncomingLikelyLost());
- QString incomingFlightTimeString = locale.toString(stats.getIncomingFlightTimeAverage());
+
+ int clockSkewInMS = node->getClockSkewUsec() / (int)USECS_PER_MSEC;
+ QString incomingFlightTimeString = locale.toString((int)stats.getIncomingFlightTimeAverage());
+ QString incomingPingTimeString = locale.toString(node->getPingMs());
+ QString incomingClockSkewString = locale.toString(clockSkewInMS);
serverDetails << "
" << "Incoming Packets: " <<
incomingPacketsString.toLocal8Bit().constData() <<
" Out of Order: " << incomingOutOfOrderString.toLocal8Bit().constData() <<
" Likely Lost: " << incomingLikelyLostString.toLocal8Bit().constData();
- serverDetails << "
" <<
+ serverDetails << "
" <<
" Average Flight Time: " << incomingFlightTimeString.toLocal8Bit().constData() << " msecs";
+
+ serverDetails << "
" <<
+ " Average Ping Time: " << incomingPingTimeString.toLocal8Bit().constData() << " msecs";
+
+ serverDetails << "
" <<
+ " Average Clock Skew: " << incomingClockSkewString.toLocal8Bit().constData() << " msecs";
serverDetails << "
" << "Incoming" <<
" Bytes: " << incomingBytesString.toLocal8Bit().constData() <<
diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp
index dfc0eac1c8..56bbc79bda 100644
--- a/libraries/octree/src/Octree.cpp
+++ b/libraries/octree/src/Octree.cpp
@@ -1342,7 +1342,7 @@ bool Octree::readFromSVOFile(const char* fileName) {
fileOk = true; // assume the file is ok
}
if (fileOk) {
- ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, wantImportProgress);
+ ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, NULL, wantImportProgress);
readBitstreamToTree(dataAt, dataLength, args);
}
delete[] entireFile;
@@ -1481,7 +1481,7 @@ void Octree::copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinat
// ask destination tree to read the bitstream
bool wantImportProgress = true;
- ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode, 0, wantImportProgress);
+ ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode, 0, NULL, wantImportProgress);
readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
}
}
diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h
index 43ba3062b3..e9851d6ae0 100644
--- a/libraries/octree/src/Octree.h
+++ b/libraries/octree/src/Octree.h
@@ -156,6 +156,7 @@ public:
bool includeExistsBits;
OctreeElement* destinationNode;
QUuid sourceUUID;
+ Node* sourceNode;
bool wantImportProgress;
ReadBitstreamToTreeParams(
@@ -163,11 +164,13 @@ public:
bool includeExistsBits = WANT_EXISTS_BITS,
OctreeElement* destinationNode = NULL,
QUuid sourceUUID = QUuid(),
+ Node* sourceNode = NULL,
bool wantImportProgress = false) :
includeColor(includeColor),
includeExistsBits(includeExistsBits),
destinationNode(destinationNode),
sourceUUID(sourceUUID),
+ sourceNode(sourceNode),
wantImportProgress(wantImportProgress)
{}
};
diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp
index 752f7870df..4ae454ef0b 100644
--- a/libraries/octree/src/OctreeEditPacketSender.cpp
+++ b/libraries/octree/src/OctreeEditPacketSender.cpp
@@ -81,7 +81,7 @@ bool OctreeEditPacketSender::serversExist() const {
}
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
-// a known nodeID. However, we also want to handle the case where the
+// a known nodeID.
void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, ssize_t length) {
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
@@ -243,6 +243,14 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PACKET_TYPE type, unsigned c
initializePacket(packetBuffer, type);
}
+ // This is really the first time we know which server/node this particular edit message
+ // is going to, so we couldn't adjust for clock skew till now. But here's our chance.
+ // We call this virtual function that allows our specific type of EditPacketSender to
+ // fixup the buffer for any clock skew
+ if (node->getClockSkewUsec() != 0) {
+ adjustEditPacketForClockSkew(codeColorBuffer, length, node->getClockSkewUsec());
+ }
+
memcpy(&packetBuffer._currentBuffer[packetBuffer._currentSize], codeColorBuffer, length);
packetBuffer._currentSize += length;
}
diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h
index 825c786a48..9539f309fd 100644
--- a/libraries/octree/src/OctreeEditPacketSender.h
+++ b/libraries/octree/src/OctreeEditPacketSender.h
@@ -86,6 +86,7 @@ public:
// you must override these...
virtual unsigned char getMyNodeType() const = 0;
+ virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { };
protected:
bool _shouldSend;
diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp
index aa436fb690..0482a38b5b 100644
--- a/libraries/octree/src/OctreeRenderer.cpp
+++ b/libraries/octree/src/OctreeRenderer.cpp
@@ -26,7 +26,7 @@ void OctreeRenderer::init() {
OctreeRenderer::~OctreeRenderer() {
}
-void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
+void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode) {
bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
bool extraDebugging = false; // Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()",showTimingDetails);
@@ -57,7 +57,8 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi
bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
- int flightTime = arrivedAt - sentAt;
+ int clockSkew = sourceNode ? sourceNode->getClockSkewUsec() : 0;
+ int flightTime = arrivedAt - sentAt + clockSkew;
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0;
int dataBytes = packetLength - OCTREE_PACKET_HEADER_SIZE;
@@ -88,7 +89,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi
if (sectionLength) {
// ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL,
- getDataSourceUUID());
+ getDataSourceUUID(), sourceNode);
_tree->lockForWrite();
OctreePacketData packetData(packetIsCompressed);
packetData.loadFinalizedContent(dataAt, sectionLength);
diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h
index 767ae48691..a29e21462c 100644
--- a/libraries/octree/src/OctreeRenderer.h
+++ b/libraries/octree/src/OctreeRenderer.h
@@ -43,7 +43,7 @@ public:
virtual void renderElement(OctreeElement* element, RenderArgs* args) = 0;
/// process incoming data
- virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
+ virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
/// initialize and GPU/rendering related resources
void init();
diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp
index 076e2cc118..9edaf63698 100644
--- a/libraries/octree/src/OctreeSceneStats.cpp
+++ b/libraries/octree/src/OctreeSceneStats.cpp
@@ -798,7 +798,8 @@ const char* OctreeSceneStats::getItemValue(Item item) {
return _itemValueBuffer;
}
-void OctreeSceneStats::trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength, bool wasStatsPacket) {
+void OctreeSceneStats::trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength,
+ bool wasStatsPacket, int nodeClockSkewUsec) {
_incomingPacket++;
_incomingBytes += messageLength;
if (!wasStatsPacket) {
@@ -820,7 +821,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(unsigned char* messageData, ssi
//bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
- int flightTime = arrivedAt - sentAt;
+ int flightTime = arrivedAt - sentAt + nodeClockSkewUsec;
const int USECS_PER_MSEC = 1000;
float flightTimeMsecs = flightTime / USECS_PER_MSEC;
_incomingFlightTimeAverage.updateAverage(flightTimeMsecs);
diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h
index 0d3490a31d..833568f6f3 100644
--- a/libraries/octree/src/OctreeSceneStats.h
+++ b/libraries/octree/src/OctreeSceneStats.h
@@ -153,7 +153,8 @@ public:
unsigned long getLastFullElapsedTime() const { return _lastFullElapsed; }
// Used in client implementations to track individual octree packets
- void trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength, bool wasStatsPacket);
+ void trackIncomingOctreePacket(unsigned char* messageData, ssize_t messageLength,
+ bool wasStatsPacket, int nodeClockSkewUsec);
unsigned int getIncomingPackets() const { return _incomingPacket; }
unsigned long getIncomingBytes() const { return _incomingBytes; }
diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp
index 83d313c95f..f2ee1c1fb4 100644
--- a/libraries/particles/src/Particle.cpp
+++ b/libraries/particles/src/Particle.cpp
@@ -12,6 +12,7 @@
#include
#include // usecTimestampNow()
+#include
#include "Particle.h"
@@ -136,6 +137,8 @@ int Particle::expectedEditMessageBytes() {
int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
int bytesRead = 0;
if (bytesLeftToRead >= expectedBytes()) {
+ int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
+
const unsigned char* dataAt = data;
// id
@@ -154,11 +157,13 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated));
dataAt += sizeof(_lastUpdated);
bytesRead += sizeof(_lastUpdated);
+ _lastUpdated -= clockSkew;
// _lastEdited
memcpy(&_lastEdited, dataAt, sizeof(_lastEdited));
dataAt += sizeof(_lastEdited);
bytesRead += sizeof(_lastEdited);
+ _lastEdited -= clockSkew;
// radius
memcpy(&_radius, dataAt, sizeof(_radius));
@@ -357,7 +362,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
sizeOut += sizeof(details[i].creatorTokenID);
}
- // radius
+ // lastEdited
memcpy(copyAt, &details[i].lastEdited, sizeof(details[i].lastEdited));
copyAt += sizeof(details[i].lastEdited);
sizeOut += sizeof(details[i].lastEdited);
@@ -420,6 +425,38 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
return success;
}
+// adjust any internal timestamps to fix clock skew for this server
+void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
+ unsigned char* dataAt = codeColorBuffer;
+ int octets = numberOfThreeBitSectionsInCode(dataAt);
+ int lengthOfOctcode = bytesRequiredForCodeLength(octets);
+ dataAt += lengthOfOctcode;
+
+ // id
+ uint32_t id;
+ memcpy(&id, dataAt, sizeof(id));
+ dataAt += sizeof(id);
+ // special case for handling "new" particles
+ if (id == NEW_PARTICLE) {
+ // If this is a NEW_PARTICLE, 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
+ dataAt += sizeof(uint32_t);
+ }
+
+ // lastEdited
+ uint64_t lastEditedInLocalTime;
+ memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime));
+ uint64_t lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
+ memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime));
+ const bool wantDebug = false;
+ if (wantDebug) {
+ qDebug("Particle::adjustEditPacketForClockSkew()...\n");
+ qDebug(" lastEditedInLocalTime: %llu\n", lastEditedInLocalTime);
+ qDebug(" clockSkew: %d\n", clockSkew);
+ qDebug(" lastEditedInServerTime: %llu\n", lastEditedInServerTime);
+ }
+}
+
void Particle::update() {
diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h
index cbf4c94bd3..0d50413b4f 100644
--- a/libraries/particles/src/Particle.h
+++ b/libraries/particles/src/Particle.h
@@ -106,6 +106,8 @@ public:
static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details,
unsigned char* bufferOut, int sizeIn, int& sizeOut);
+
+ static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
void update();
diff --git a/libraries/particles/src/ParticleEditPacketSender.cpp b/libraries/particles/src/ParticleEditPacketSender.cpp
index fcf00d75f3..bd56728176 100644
--- a/libraries/particles/src/ParticleEditPacketSender.cpp
+++ b/libraries/particles/src/ParticleEditPacketSender.cpp
@@ -37,6 +37,11 @@ void ParticleEditPacketSender::sendEditParticleMessage(PACKET_TYPE type, const P
}
}
+void ParticleEditPacketSender::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) {
+ Particle::adjustEditPacketForClockSkew(codeColorBuffer, length, clockSkew);
+}
+
+
void ParticleEditPacketSender::queueParticleEditMessages(PACKET_TYPE type, int numberOfDetails, ParticleDetail* details) {
if (!_shouldSend) {
return; // bail early
diff --git a/libraries/particles/src/ParticleEditPacketSender.h b/libraries/particles/src/ParticleEditPacketSender.h
index 6a21ca3ae2..2295ee22b2 100644
--- a/libraries/particles/src/ParticleEditPacketSender.h
+++ b/libraries/particles/src/ParticleEditPacketSender.h
@@ -28,7 +28,8 @@ public:
/// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known.
void queueParticleEditMessages(PACKET_TYPE type, int numberOfDetails, ParticleDetail* details);
- // My server type is the voxel server
+ // My server type is the particle server
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
+ virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
};
#endif // __shared__ParticleEditPacketSender__
diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp
index e770347fd8..cedacd4be1 100644
--- a/libraries/particles/src/ParticleTree.cpp
+++ b/libraries/particles/src/ParticleTree.cpp
@@ -47,7 +47,7 @@ bool ParticleTree::findAndUpdateOperation(OctreeElement* element, void* extraDat
return true;
}
-void ParticleTree::storeParticle(const Particle& particle) {
+void ParticleTree::storeParticle(const Particle& particle, Node* senderNode) {
// First, look for the existing particle in the tree..
FindAndUpdateParticleArgs args = { particle, false };
recurseTreeWithOperation(findAndUpdateOperation, &args);
@@ -58,7 +58,7 @@ void ParticleTree::storeParticle(const Particle& particle) {
float size = particle.getRadius();
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
- element->storeParticle(particle);
+ element->storeParticle(particle, senderNode);
}
// what else do we need to do here to get reaveraging to work
_isDirty = true;
@@ -165,7 +165,7 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p
switch (packetType) {
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: {
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes);
- storeParticle(newParticle);
+ storeParticle(newParticle, senderNode);
if (newParticle.isNewlyCreated()) {
notifyNewlyCreatedParticle(newParticle, senderNode);
}
diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h
index 64d6982322..3f8b7fda09 100644
--- a/libraries/particles/src/ParticleTree.h
+++ b/libraries/particles/src/ParticleTree.h
@@ -44,7 +44,7 @@ public:
virtual void update();
- void storeParticle(const Particle& particle);
+ void storeParticle(const Particle& particle, Node* senderNode = NULL);
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
const Particle* findParticleByID(uint32_t id);
diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp
index 3d1a552d17..b7bc89172e 100644
--- a/libraries/particles/src/ParticleTreeElement.cpp
+++ b/libraries/particles/src/ParticleTreeElement.cpp
@@ -128,7 +128,7 @@ bool ParticleTreeElement::containsParticle(const Particle& particle) const {
}
bool ParticleTreeElement::updateParticle(const Particle& particle) {
- const bool wantDebug = false;
+ const bool wantDebug = true;
uint16_t numberOfParticles = _particles.size();
for (uint16_t i = 0; i < numberOfParticles; i++) {
if (_particles[i].getID() == particle.getID()) {
@@ -230,7 +230,7 @@ bool ParticleTreeElement::collapseChildren() {
}
-void ParticleTreeElement::storeParticle(const Particle& particle) {
+void ParticleTreeElement::storeParticle(const Particle& particle, Node* senderNode) {
_particles.push_back(particle);
markWithChangedTime();
}
diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h
index 0f9b3682c1..34d4054d98 100644
--- a/libraries/particles/src/ParticleTreeElement.h
+++ b/libraries/particles/src/ParticleTreeElement.h
@@ -89,7 +89,7 @@ public:
protected:
- void storeParticle(const Particle& particle);
+ void storeParticle(const Particle& particle, Node* senderNode = NULL);
ParticleTree* _myTree;
std::vector _particles;
diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp
index e681a20277..70379801c6 100644
--- a/libraries/shared/src/Node.cpp
+++ b/libraries/shared/src/Node.cpp
@@ -32,7 +32,8 @@ Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const
_activeSocket(NULL),
_bytesReceivedMovingAverage(NULL),
_linkedData(NULL),
- _isAlive(true)
+ _isAlive(true),
+ _clockSkewUsec(0)
{
pthread_mutex_init(&_mutex, 0);
}
diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h
index 4614a6f0a8..96f16a31d4 100644
--- a/libraries/shared/src/Node.h
+++ b/libraries/shared/src/Node.h
@@ -68,6 +68,9 @@ public:
int getPingMs() const { return _pingMs; }
void setPingMs(int pingMs) { _pingMs = pingMs; }
+
+ int getClockSkewUsec() const { return _clockSkewUsec; }
+ void setClockSkewUsec(int clockSkew) { _clockSkewUsec = clockSkew; }
void lock() { pthread_mutex_lock(&_mutex); }
@@ -93,6 +96,7 @@ private:
NodeData* _linkedData;
bool _isAlive;
int _pingMs;
+ int _clockSkewUsec;
pthread_mutex_t _mutex;
};
diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp
index d8661d9350..c7e7a2c3f0 100644
--- a/libraries/shared/src/NodeList.cpp
+++ b/libraries/shared/src/NodeList.cpp
@@ -112,9 +112,33 @@ void NodeList::timePingReply(const HifiSockAddr& nodeAddress, unsigned char *pac
if (node->getPublicSocket() == nodeAddress ||
node->getLocalSocket() == nodeAddress) {
- int pingTime = usecTimestampNow() - *(uint64_t*)(packetData + numBytesForPacketHeader(packetData));
+ unsigned char* dataAt = packetData + numBytesForPacketHeader(packetData);
+ uint64_t ourOriginalTime = *(uint64_t*)(dataAt);
+ dataAt += sizeof(ourOriginalTime);
+ uint64_t othersReplyTime = *(uint64_t*)(dataAt);
+ uint64_t now = usecTimestampNow();
+ int pingTime = now - ourOriginalTime;
+ int oneWayFlightTime = pingTime/2; // half of the ping is our one way flight
+
+ // The other node's expected time should be our original time plus the one way flight time
+ // anything other than that is clock skew
+ uint64_t othersExprectedReply = ourOriginalTime + oneWayFlightTime;
+ int clockSkew = othersReplyTime - othersExprectedReply;
node->setPingMs(pingTime / 1000);
+ node->setClockSkewUsec(clockSkew);
+
+ const bool wantDebug = false;
+ if (wantDebug) {
+ qDebug() << "PING_REPLY from node " << *node << "\n" <<
+ " now: " << now << "\n" <<
+ " ourTime: " << ourOriginalTime << "\n" <<
+ " pingTime: " << pingTime << "\n" <<
+ " oneWayFlightTime: " << oneWayFlightTime << "\n" <<
+ " othersReplyTime: " << othersReplyTime << "\n" <<
+ " othersExprectedReply: " << othersExprectedReply << "\n" <<
+ " clockSkew: " << clockSkew << "\n";
+ }
break;
}
}
@@ -131,9 +155,11 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, unsigned char
break;
}
case PACKET_TYPE_PING: {
- // send it right back
- populateTypeAndVersion(packetData, PACKET_TYPE_PING_REPLY);
- _nodeSocket.writeDatagram((char*) packetData, dataBytes, senderSockAddr.getAddress(), senderSockAddr.getPort());
+ // send back a reply
+ unsigned char replyPacket[MAX_PACKET_SIZE];
+ int replyPacketLength = fillPingReplyPacket(packetData, replyPacket);
+ _nodeSocket.writeDatagram((char*)replyPacket, replyPacketLength,
+ senderSockAddr.getAddress(), senderSockAddr.getPort());
break;
}
case PACKET_TYPE_PING_REPLY: {
@@ -616,21 +642,42 @@ void NodeList::sendAssignment(Assignment& assignment) {
assignmentServerSocket->getPort());
}
+int NodeList::fillPingPacket(unsigned char* buffer) {
+ int numHeaderBytes = populateTypeAndVersion(buffer, PACKET_TYPE_PING);
+ uint64_t currentTime = usecTimestampNow();
+ memcpy(buffer + numHeaderBytes, ¤tTime, sizeof(currentTime));
+ return numHeaderBytes + sizeof(currentTime);
+}
+
+int NodeList::fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer) {
+ int numHeaderBytesOriginal = numBytesForPacketHeader(pingBuffer);
+ uint64_t timeFromOriginalPing = *(uint64_t*)(pingBuffer + numHeaderBytesOriginal);
+
+ int numHeaderBytesReply = populateTypeAndVersion(replyBuffer, PACKET_TYPE_PING_REPLY);
+ int length = numHeaderBytesReply;
+ uint64_t ourReplyTime = usecTimestampNow();
+
+ unsigned char* dataAt = replyBuffer + numHeaderBytesReply;
+ memcpy(dataAt, &timeFromOriginalPing, sizeof(timeFromOriginalPing));
+ dataAt += sizeof(timeFromOriginalPing);
+ length += sizeof(timeFromOriginalPing);
+
+ memcpy(dataAt, &ourReplyTime, sizeof(ourReplyTime));
+ dataAt += sizeof(ourReplyTime);
+ length += sizeof(ourReplyTime);
+
+ return length;
+}
+
+
void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) {
-
- uint64_t currentTime = 0;
-
- // setup a ping packet to send to this node
- unsigned char pingPacket[numBytesForPacketHeader((uchar*) &PACKET_TYPE_PING) + sizeof(currentTime)];
- int numHeaderBytes = populateTypeAndVersion(pingPacket, PACKET_TYPE_PING);
-
- currentTime = usecTimestampNow();
- memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime));
+ unsigned char pingPacket[MAX_PACKET_SIZE];
+ int pingPacketLength = fillPingPacket(pingPacket);
// send the ping packet to the local and public sockets for this node
- _nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket),
+ _nodeSocket.writeDatagram((char*) pingPacket, pingPacketLength,
node->getLocalSocket().getAddress(), node->getLocalSocket().getPort());
- _nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket),
+ _nodeSocket.writeDatagram((char*) pingPacket, pingPacketLength,
node->getPublicSocket().getAddress(), node->getPublicSocket().getPort());
}
diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h
index 9ee4c8519d..47cdbac99d 100644
--- a/libraries/shared/src/NodeList.h
+++ b/libraries/shared/src/NodeList.h
@@ -103,6 +103,8 @@ public:
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
void sendAssignment(Assignment& assignment);
+ int fillPingPacket(unsigned char* buffer);
+ int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer);
void pingPublicAndLocalSocketsForInactiveNode(Node* node);
void sendKillNode(const char* nodeTypes, int numNodeTypes);
diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp
index 05e4b4e21d..07cdbf76fa 100644
--- a/libraries/shared/src/PacketHeaders.cpp
+++ b/libraries/shared/src/PacketHeaders.cpp
@@ -56,6 +56,9 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
case PACKET_TYPE_PARTICLE_DATA:
return 5;
+ case PACKET_TYPE_PING_REPLY:
+ return 1;
+
default:
return 0;
}
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index 893922ec28..df04475eba 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -52,7 +52,9 @@ static const float METER = 1.0f;
static const float DECIMETER = 0.1f;
static const float CENTIMETER = 0.01f;
static const float MILLIIMETER = 0.001f;
-static const uint64_t USECS_PER_SECOND = 1000 * 1000;
+static const uint64_t USECS_PER_MSEC = 1000;
+static const uint64_t MSECS_PER_SECOND = 1000;
+static const uint64_t USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
uint64_t usecTimestamp(const timeval *time);
uint64_t usecTimestampNow();