mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 18:50:00 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels
This commit is contained in:
commit
49ab3d6b43
23 changed files with 587 additions and 151 deletions
|
@ -33,6 +33,7 @@ public:
|
||||||
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
||||||
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
|
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
|
||||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
|
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
|
||||||
|
virtual PacketType getMyEditNackType() const { return PacketTypeModelEditNack; }
|
||||||
|
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun();
|
virtual void beforeRun();
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
#include "OctreeInboundPacketProcessor.h"
|
#include "OctreeInboundPacketProcessor.h"
|
||||||
|
|
||||||
static QUuid DEFAULT_NODE_ID_REF;
|
static QUuid DEFAULT_NODE_ID_REF;
|
||||||
|
const quint64 TOO_LONG_SINCE_LAST_NACK = 1 * USECS_PER_SECOND;
|
||||||
|
|
||||||
OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServer) :
|
OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServer) :
|
||||||
_myServer(myServer),
|
_myServer(myServer),
|
||||||
|
@ -25,7 +27,8 @@ OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServe
|
||||||
_totalProcessTime(0),
|
_totalProcessTime(0),
|
||||||
_totalLockWaitTime(0),
|
_totalLockWaitTime(0),
|
||||||
_totalElementsInPacket(0),
|
_totalElementsInPacket(0),
|
||||||
_totalPackets(0)
|
_totalPackets(0),
|
||||||
|
_lastNackTime(usecTimestampNow())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,10 +38,38 @@ void OctreeInboundPacketProcessor::resetStats() {
|
||||||
_totalLockWaitTime = 0;
|
_totalLockWaitTime = 0;
|
||||||
_totalElementsInPacket = 0;
|
_totalElementsInPacket = 0;
|
||||||
_totalPackets = 0;
|
_totalPackets = 0;
|
||||||
|
_lastNackTime = usecTimestampNow();
|
||||||
|
|
||||||
_singleSenderStats.clear();
|
_singleSenderStats.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long OctreeInboundPacketProcessor::getMaxWait() const {
|
||||||
|
// calculate time until next sendNackPackets()
|
||||||
|
quint64 nextNackTime = _lastNackTime + TOO_LONG_SINCE_LAST_NACK;
|
||||||
|
quint64 now = usecTimestampNow();
|
||||||
|
if (now >= nextNackTime) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (nextNackTime - now) / USECS_PER_MSEC + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeInboundPacketProcessor::preProcess() {
|
||||||
|
// check if it's time to send a nack. If yes, do so
|
||||||
|
quint64 now = usecTimestampNow();
|
||||||
|
if (now - _lastNackTime >= TOO_LONG_SINCE_LAST_NACK) {
|
||||||
|
_lastNackTime = now;
|
||||||
|
sendNackPackets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeInboundPacketProcessor::midProcess() {
|
||||||
|
// check if it's time to send a nack. If yes, do so
|
||||||
|
quint64 now = usecTimestampNow();
|
||||||
|
if (now - _lastNackTime >= TOO_LONG_SINCE_LAST_NACK) {
|
||||||
|
_lastNackTime = now;
|
||||||
|
sendNackPackets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
|
void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
|
||||||
|
|
||||||
|
@ -123,13 +154,13 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin
|
||||||
qDebug() << "sender has no known nodeUUID.";
|
qDebug() << "sender has no known nodeUUID.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trackInboundPackets(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
|
trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
|
||||||
} else {
|
} else {
|
||||||
qDebug("unknown packet ignored... packetType=%d", packetType);
|
qDebug("unknown packet ignored... packetType=%d", packetType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, quint64 transitTime,
|
void OctreeInboundPacketProcessor::trackInboundPacket(const QUuid& nodeUUID, unsigned short int sequence, quint64 transitTime,
|
||||||
int editsInPacket, quint64 processTime, quint64 lockWaitTime) {
|
int editsInPacket, quint64 processTime, quint64 lockWaitTime) {
|
||||||
|
|
||||||
_totalTransitTime += transitTime;
|
_totalTransitTime += transitTime;
|
||||||
|
@ -142,31 +173,174 @@ void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, in
|
||||||
// see if this is the first we've heard of this node...
|
// see if this is the first we've heard of this node...
|
||||||
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
|
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
|
||||||
SingleSenderStats stats;
|
SingleSenderStats stats;
|
||||||
|
stats.trackInboundPacket(sequence, transitTime, editsInPacket, processTime, lockWaitTime);
|
||||||
stats._totalTransitTime += transitTime;
|
|
||||||
stats._totalProcessTime += processTime;
|
|
||||||
stats._totalLockWaitTime += lockWaitTime;
|
|
||||||
stats._totalElementsInPacket += editsInPacket;
|
|
||||||
stats._totalPackets++;
|
|
||||||
|
|
||||||
_singleSenderStats[nodeUUID] = stats;
|
_singleSenderStats[nodeUUID] = stats;
|
||||||
} else {
|
} else {
|
||||||
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
|
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
|
||||||
stats._totalTransitTime += transitTime;
|
stats.trackInboundPacket(sequence, transitTime, editsInPacket, processTime, lockWaitTime);
|
||||||
stats._totalProcessTime += processTime;
|
|
||||||
stats._totalLockWaitTime += lockWaitTime;
|
|
||||||
stats._totalElementsInPacket += editsInPacket;
|
|
||||||
stats._totalPackets++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int OctreeInboundPacketProcessor::sendNackPackets() {
|
||||||
|
|
||||||
SingleSenderStats::SingleSenderStats() {
|
int packetsSent = 0;
|
||||||
_totalTransitTime = 0;
|
|
||||||
_totalProcessTime = 0;
|
NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
|
||||||
_totalLockWaitTime = 0;
|
while (i != _singleSenderStats.end()) {
|
||||||
_totalElementsInPacket = 0;
|
|
||||||
_totalPackets = 0;
|
QUuid nodeUUID = i.key();
|
||||||
|
SingleSenderStats nodeStats = i.value();
|
||||||
|
|
||||||
|
// check if this node is still alive. Remove its stats if it's dead.
|
||||||
|
if (!isAlive(nodeUUID)) {
|
||||||
|
i = _singleSenderStats.erase(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are packets from _node that are waiting to be processed,
|
||||||
|
// don't send a NACK since the missing packets may be among those waiting packets.
|
||||||
|
if (hasPacketsToProcessFrom(nodeUUID)) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SharedNodePointer& destinationNode = NodeList::getInstance()->getNodeHash().value(nodeUUID);
|
||||||
|
const QSet<unsigned short int>& missingSequenceNumbers = nodeStats.getMissingSequenceNumbers();
|
||||||
|
|
||||||
|
// check if there are any sequence numbers that need to be nacked
|
||||||
|
int numSequenceNumbersAvailable = missingSequenceNumbers.size();
|
||||||
|
|
||||||
|
// construct nack packet(s) for this node
|
||||||
|
|
||||||
|
QSet<unsigned short int>::const_iterator missingSequenceNumberIterator = missingSequenceNumbers.begin();
|
||||||
|
char packet[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
while (numSequenceNumbersAvailable > 0) {
|
||||||
|
|
||||||
|
char* dataAt = packet;
|
||||||
|
int bytesRemaining = MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
// pack header
|
||||||
|
int numBytesPacketHeader = populatePacketHeader(packet, _myServer->getMyEditNackType());
|
||||||
|
dataAt += numBytesPacketHeader;
|
||||||
|
bytesRemaining -= numBytesPacketHeader;
|
||||||
|
|
||||||
|
// calculate and pack the number of sequence numbers to nack
|
||||||
|
int numSequenceNumbersRoomFor = (bytesRemaining - sizeof(uint16_t)) / sizeof(unsigned short int);
|
||||||
|
uint16_t numSequenceNumbers = std::min(numSequenceNumbersAvailable, numSequenceNumbersRoomFor);
|
||||||
|
uint16_t* numSequenceNumbersAt = (uint16_t*)dataAt;
|
||||||
|
*numSequenceNumbersAt = numSequenceNumbers;
|
||||||
|
dataAt += sizeof(uint16_t);
|
||||||
|
|
||||||
|
// pack sequence numbers to nack
|
||||||
|
for (uint16_t i = 0; i < numSequenceNumbers; i++) {
|
||||||
|
unsigned short int* sequenceNumberAt = (unsigned short int*)dataAt;
|
||||||
|
*sequenceNumberAt = *missingSequenceNumberIterator;
|
||||||
|
dataAt += sizeof(unsigned short int);
|
||||||
|
|
||||||
|
missingSequenceNumberIterator++;
|
||||||
|
}
|
||||||
|
numSequenceNumbersAvailable -= numSequenceNumbers;
|
||||||
|
|
||||||
|
// send it
|
||||||
|
qint64 bytesWritten = NodeList::getInstance()->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode);
|
||||||
|
|
||||||
|
packetsSent++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return packetsSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SingleSenderStats::SingleSenderStats()
|
||||||
|
: _totalTransitTime(0),
|
||||||
|
_totalProcessTime(0),
|
||||||
|
_totalLockWaitTime(0),
|
||||||
|
_totalElementsInPacket(0),
|
||||||
|
_totalPackets(0),
|
||||||
|
_incomingLastSequence(0),
|
||||||
|
_missingSequenceNumbers()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleSenderStats::trackInboundPacket(unsigned short int incomingSequence, quint64 transitTime,
|
||||||
|
int editsInPacket, quint64 processTime, quint64 lockWaitTime) {
|
||||||
|
|
||||||
|
const int UINT16_RANGE = std::numeric_limits<uint16_t>::max() + 1;
|
||||||
|
const int MAX_REASONABLE_SEQUENCE_GAP = 1000; // this must be less than UINT16_RANGE / 2 for rollover handling to work
|
||||||
|
const int MAX_MISSING_SEQUENCE_SIZE = 100;
|
||||||
|
|
||||||
|
unsigned short int expectedSequence = _totalPackets == 0 ? incomingSequence : _incomingLastSequence + (unsigned short int)1;
|
||||||
|
|
||||||
|
if (incomingSequence == expectedSequence) { // on time
|
||||||
|
_incomingLastSequence = incomingSequence;
|
||||||
|
} else { // out of order
|
||||||
|
int incoming = (int)incomingSequence;
|
||||||
|
int expected = (int)expectedSequence;
|
||||||
|
|
||||||
|
// check if the gap between incoming and expected is reasonable, taking possible rollover into consideration
|
||||||
|
int absGap = std::abs(incoming - expected);
|
||||||
|
if (absGap >= UINT16_RANGE - MAX_REASONABLE_SEQUENCE_GAP) {
|
||||||
|
// rollover likely occurred between incoming and expected.
|
||||||
|
// correct the larger of the two so that it's within [-UINT16_RANGE, -1] while the other remains within [0, UINT16_RANGE-1]
|
||||||
|
if (incoming > expected) {
|
||||||
|
incoming -= UINT16_RANGE;
|
||||||
|
} else {
|
||||||
|
expected -= UINT16_RANGE;
|
||||||
|
}
|
||||||
|
} else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) {
|
||||||
|
// ignore packet if gap is unreasonable
|
||||||
|
qDebug() << "ignoring unreasonable packet... sequence:" << incomingSequence
|
||||||
|
<< "_incomingLastSequence:" << _incomingLastSequence;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now that rollover has been corrected for (if it occurred), incoming and expected can be
|
||||||
|
// compared to each other directly, though one of them might be negative
|
||||||
|
if (incoming > expected) { // early
|
||||||
|
// add all sequence numbers that were skipped to the missing sequence numbers list
|
||||||
|
for (int missingSequence = expected; missingSequence < incoming; missingSequence++) {
|
||||||
|
_missingSequenceNumbers.insert(missingSequence < 0 ? missingSequence + UINT16_RANGE : missingSequence);
|
||||||
|
}
|
||||||
|
_incomingLastSequence = incomingSequence;
|
||||||
|
} else { // late
|
||||||
|
// remove this from missing sequence number if it's in there
|
||||||
|
_missingSequenceNumbers.remove(incomingSequence);
|
||||||
|
|
||||||
|
// do not update _incomingLastSequence; it shouldn't become smaller
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prune missing sequence list if it gets too big; sequence numbers that are older than MAX_REASONABLE_SEQUENCE_GAP
|
||||||
|
// will be removed.
|
||||||
|
if (_missingSequenceNumbers.size() > MAX_MISSING_SEQUENCE_SIZE) {
|
||||||
|
// some older sequence numbers may be from before a rollover point; this must be handled.
|
||||||
|
// some sequence numbers in this list may be larger than _incomingLastSequence, indicating that they were received
|
||||||
|
// before the most recent rollover.
|
||||||
|
int cutoff = (int)_incomingLastSequence - MAX_REASONABLE_SEQUENCE_GAP;
|
||||||
|
if (cutoff >= 0) {
|
||||||
|
foreach(unsigned short int missingSequence, _missingSequenceNumbers) {
|
||||||
|
unsigned short int nonRolloverCutoff = (unsigned short int)cutoff;
|
||||||
|
if (missingSequence > _incomingLastSequence || missingSequence <= nonRolloverCutoff) {
|
||||||
|
_missingSequenceNumbers.remove(missingSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsigned short int rolloverCutoff = (unsigned short int)(cutoff + UINT16_RANGE);
|
||||||
|
foreach(unsigned short int missingSequence, _missingSequenceNumbers) {
|
||||||
|
if (missingSequence > _incomingLastSequence && missingSequence <= rolloverCutoff) {
|
||||||
|
_missingSequenceNumbers.remove(missingSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update other stats
|
||||||
|
_totalTransitTime += transitTime;
|
||||||
|
_totalProcessTime += processTime;
|
||||||
|
_totalLockWaitTime += lockWaitTime;
|
||||||
|
_totalElementsInPacket += editsInPacket;
|
||||||
|
_totalPackets++;
|
||||||
|
}
|
||||||
|
|
|
@ -32,16 +32,24 @@ public:
|
||||||
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
|
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
|
||||||
quint64 getAverageLockWaitTimePerElement() const
|
quint64 getAverageLockWaitTimePerElement() const
|
||||||
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
|
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
|
||||||
|
const QSet<unsigned short int>& getMissingSequenceNumbers() const { return _missingSequenceNumbers; }
|
||||||
|
|
||||||
|
void trackInboundPacket(unsigned short int incomingSequence, quint64 transitTime,
|
||||||
|
int editsInPacket, quint64 processTime, quint64 lockWaitTime);
|
||||||
|
|
||||||
quint64 _totalTransitTime;
|
quint64 _totalTransitTime;
|
||||||
quint64 _totalProcessTime;
|
quint64 _totalProcessTime;
|
||||||
quint64 _totalLockWaitTime;
|
quint64 _totalLockWaitTime;
|
||||||
quint64 _totalElementsInPacket;
|
quint64 _totalElementsInPacket;
|
||||||
quint64 _totalPackets;
|
quint64 _totalPackets;
|
||||||
|
|
||||||
|
unsigned short int _incomingLastSequence;
|
||||||
|
QSet<unsigned short int> _missingSequenceNumbers;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<QUuid, SingleSenderStats> NodeToSenderStatsMap;
|
typedef QHash<QUuid, SingleSenderStats> NodeToSenderStatsMap;
|
||||||
typedef std::map<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterator;
|
typedef QHash<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterator;
|
||||||
|
typedef QHash<QUuid, SingleSenderStats>::const_iterator NodeToSenderStatsMapConstIterator;
|
||||||
|
|
||||||
|
|
||||||
/// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes
|
/// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes
|
||||||
|
@ -66,10 +74,18 @@ public:
|
||||||
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
|
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
|
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
|
||||||
|
|
||||||
|
virtual unsigned long getMaxWait() const;
|
||||||
|
virtual void preProcess();
|
||||||
|
virtual void midProcess();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void trackInboundPackets(const QUuid& nodeUUID, int sequence, quint64 transitTime,
|
int sendNackPackets();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void trackInboundPacket(const QUuid& nodeUUID, unsigned short int sequence, quint64 transitTime,
|
||||||
int voxelsInPacket, quint64 processTime, quint64 lockWaitTime);
|
int voxelsInPacket, quint64 processTime, quint64 lockWaitTime);
|
||||||
|
|
||||||
OctreeServer* _myServer;
|
OctreeServer* _myServer;
|
||||||
|
@ -82,5 +98,7 @@ private:
|
||||||
quint64 _totalPackets;
|
quint64 _totalPackets;
|
||||||
|
|
||||||
NodeToSenderStatsMap _singleSenderStats;
|
NodeToSenderStatsMap _singleSenderStats;
|
||||||
|
|
||||||
|
quint64 _lastNackTime;
|
||||||
};
|
};
|
||||||
#endif // hifi_OctreeInboundPacketProcessor_h
|
#endif // hifi_OctreeInboundPacketProcessor_h
|
||||||
|
|
|
@ -648,10 +648,10 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
int senderNumber = 0;
|
int senderNumber = 0;
|
||||||
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
|
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
|
||||||
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
|
for (NodeToSenderStatsMapConstIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
|
||||||
senderNumber++;
|
senderNumber++;
|
||||||
QUuid senderID = i->first;
|
QUuid senderID = i.key();
|
||||||
SingleSenderStats& senderStats = i->second;
|
const SingleSenderStats& senderStats = i.value();
|
||||||
|
|
||||||
statsString += QString("\r\n Stats for sender %1 uuid: %2\r\n")
|
statsString += QString("\r\n Stats for sender %1 uuid: %2\r\n")
|
||||||
.arg(senderNumber).arg(senderID.toString());
|
.arg(senderNumber).arg(senderID.toString());
|
||||||
|
@ -1060,6 +1060,9 @@ void OctreeServer::nodeAdded(SharedNodePointer node) {
|
||||||
void OctreeServer::nodeKilled(SharedNodePointer node) {
|
void OctreeServer::nodeKilled(SharedNodePointer node) {
|
||||||
quint64 start = usecTimestampNow();
|
quint64 start = usecTimestampNow();
|
||||||
|
|
||||||
|
// calling this here since nodeKilled slot in ReceivedPacketProcessor can't be triggered by signals yet!!
|
||||||
|
_octreeInboundPacketProcessor->nodeKilled(node);
|
||||||
|
|
||||||
qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
|
qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
|
||||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
|
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
|
|
|
@ -68,6 +68,7 @@ public:
|
||||||
virtual const char* getMyServerName() const = 0;
|
virtual const char* getMyServerName() const = 0;
|
||||||
virtual const char* getMyLoggingServerTargetName() const = 0;
|
virtual const char* getMyLoggingServerTargetName() const = 0;
|
||||||
virtual const char* getMyDefaultPersistFilename() const = 0;
|
virtual const char* getMyDefaultPersistFilename() const = 0;
|
||||||
|
virtual PacketType getMyEditNackType() const = 0;
|
||||||
|
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun() { };
|
virtual void beforeRun() { };
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
//
|
|
||||||
// SentPacketHistory.cpp
|
|
||||||
// assignement-client/src/octree
|
|
||||||
//
|
|
||||||
// Created by Yixin Wang on 6/5/2014
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "SentPacketHistory.h"
|
|
||||||
|
|
||||||
SentPacketHistory::SentPacketHistory(int size)
|
|
||||||
: _sentPackets(size),
|
|
||||||
_newestPacketAt(0),
|
|
||||||
_numExistingPackets(0),
|
|
||||||
_newestSequenceNumber(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SentPacketHistory::packetSent(OCTREE_PACKET_SEQUENCE sequenceNumber, const QByteArray& packet) {
|
|
||||||
_newestSequenceNumber = sequenceNumber;
|
|
||||||
|
|
||||||
// increment _newestPacketAt cyclically, insert new packet there.
|
|
||||||
// this will overwrite the oldest packet in the buffer
|
|
||||||
_newestPacketAt = (_newestPacketAt == _sentPackets.size() - 1) ? 0 : _newestPacketAt + 1;
|
|
||||||
_sentPackets[_newestPacketAt] = packet;
|
|
||||||
if (_numExistingPackets < _sentPackets.size()) {
|
|
||||||
_numExistingPackets++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const QByteArray* SentPacketHistory::getPacket(OCTREE_PACKET_SEQUENCE sequenceNumber) const {
|
|
||||||
OCTREE_PACKET_SEQUENCE seqDiff = _newestSequenceNumber - sequenceNumber;
|
|
||||||
if (!(seqDiff >= 0 && seqDiff < _numExistingPackets)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
int packetAt = _newestPacketAt - seqDiff;
|
|
||||||
if (packetAt < 0) {
|
|
||||||
packetAt += _sentPackets.size();
|
|
||||||
}
|
|
||||||
return &_sentPackets.at(packetAt);
|
|
||||||
}
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
|
virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
|
||||||
virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
|
virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
|
||||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; }
|
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; }
|
||||||
|
virtual PacketType getMyEditNackType() const { return PacketTypeParticleEditNack; }
|
||||||
|
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun();
|
virtual void beforeRun();
|
||||||
|
|
|
@ -42,6 +42,7 @@ public:
|
||||||
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
||||||
virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
|
virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
|
||||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; }
|
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; }
|
||||||
|
virtual PacketType getMyEditNackType() const { return PacketTypeVoxelEditNack; }
|
||||||
|
|
||||||
// subclass may implement these method
|
// subclass may implement these method
|
||||||
virtual void beforeRun();
|
virtual void beforeRun();
|
||||||
|
|
|
@ -717,8 +717,8 @@ function rayPlaneIntersection(pickRay, point, normal) {
|
||||||
function Tooltip() {
|
function Tooltip() {
|
||||||
this.x = 285;
|
this.x = 285;
|
||||||
this.y = 115;
|
this.y = 115;
|
||||||
this.width = 110;
|
this.width = 500;
|
||||||
this.height = 115 ;
|
this.height = 145 ;
|
||||||
this.margin = 5;
|
this.margin = 5;
|
||||||
this.decimals = 3;
|
this.decimals = 3;
|
||||||
|
|
||||||
|
@ -746,6 +746,9 @@ function Tooltip() {
|
||||||
text += "yaw: " + angles.y.toFixed(this.decimals) + "\n"
|
text += "yaw: " + angles.y.toFixed(this.decimals) + "\n"
|
||||||
text += "roll: " + angles.z.toFixed(this.decimals) + "\n"
|
text += "roll: " + angles.z.toFixed(this.decimals) + "\n"
|
||||||
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
|
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
|
||||||
|
text += "ID: " + properties.id + "\n"
|
||||||
|
text += "model url: " + properties.modelURL + "\n"
|
||||||
|
text += "animation url: " + properties.animationURL + "\n"
|
||||||
|
|
||||||
Overlays.editOverlay(this.textOverlay, { text: text });
|
Overlays.editOverlay(this.textOverlay, { text: text });
|
||||||
}
|
}
|
||||||
|
@ -1019,9 +1022,11 @@ var modelMenuAddedDelete = false;
|
||||||
function setupModelMenus() {
|
function setupModelMenus() {
|
||||||
print("setupModelMenus()");
|
print("setupModelMenus()");
|
||||||
// add our menuitems
|
// add our menuitems
|
||||||
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" });
|
||||||
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Edit Properties...",
|
||||||
|
shortcutKeyEvent: { text: "`" }, afterItem: "Models" });
|
||||||
if (!Menu.menuItemExists("Edit","Delete")) {
|
if (!Menu.menuItemExists("Edit","Delete")) {
|
||||||
print("no delete... adding ours");
|
print("no delete... adding ours");
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" });
|
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
|
||||||
shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" });
|
shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" });
|
||||||
modelMenuAddedDelete = true;
|
modelMenuAddedDelete = true;
|
||||||
|
@ -1031,9 +1036,10 @@ function setupModelMenus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupModelMenus() {
|
function cleanupModelMenus() {
|
||||||
|
Menu.removeSeparator("Edit", "Models");
|
||||||
|
Menu.removeMenuItem("Edit", "Edit Properties...");
|
||||||
if (modelMenuAddedDelete) {
|
if (modelMenuAddedDelete) {
|
||||||
// delete our menuitems
|
// delete our menuitems
|
||||||
Menu.removeSeparator("Edit", "Models");
|
|
||||||
Menu.removeMenuItem("Edit", "Delete");
|
Menu.removeMenuItem("Edit", "Delete");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1054,7 +1060,8 @@ Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||||
|
|
||||||
setupModelMenus();
|
setupModelMenus();
|
||||||
Menu.menuItemEvent.connect(function(menuItem){
|
|
||||||
|
function handeMenuEvent(menuItem){
|
||||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||||
if (menuItem == "Delete") {
|
if (menuItem == "Delete") {
|
||||||
if (leftController.grabbing) {
|
if (leftController.grabbing) {
|
||||||
|
@ -1072,8 +1079,36 @@ Menu.menuItemEvent.connect(function(menuItem){
|
||||||
} else {
|
} else {
|
||||||
print(" Delete Model.... not holding...");
|
print(" Delete Model.... not holding...");
|
||||||
}
|
}
|
||||||
|
} else if (menuItem == "Edit Properties...") {
|
||||||
|
var editModelID = -1;
|
||||||
|
if (leftController.grabbing) {
|
||||||
|
print(" Edit Properties.... leftController.modelID="+ leftController.modelID);
|
||||||
|
editModelID = leftController.modelID;
|
||||||
|
} else if (rightController.grabbing) {
|
||||||
|
print(" Edit Properties.... rightController.modelID="+ rightController.modelID);
|
||||||
|
editModelID = rightController.modelID;
|
||||||
|
} else if (modelSelected) {
|
||||||
|
print(" Edit Properties.... selectedModelID="+ selectedModelID);
|
||||||
|
editModelID = selectedModelID;
|
||||||
|
} else {
|
||||||
|
print(" Edit Properties.... not holding...");
|
||||||
|
}
|
||||||
|
if (editModelID != -1) {
|
||||||
|
print(" Edit Properties.... about to edit properties...");
|
||||||
|
var propertyName = Window.prompt("Which property would you like to change?", "modelURL");
|
||||||
|
var properties = Models.getModelProperties(editModelID);
|
||||||
|
var oldValue = properties[propertyName];
|
||||||
|
var newValue = Window.prompt("New value for: " + propertyName, oldValue);
|
||||||
|
if (newValue != NULL) {
|
||||||
|
properties[propertyName] = newValue;
|
||||||
|
Models.editModel(editModelID, properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tooltip.show(false);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
Menu.menuItemEvent.connect(handeMenuEvent);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// handling of inspect.js concurrence
|
// handling of inspect.js concurrence
|
||||||
|
@ -1107,4 +1142,11 @@ Controller.keyReleaseEvent.connect(function(event) {
|
||||||
xIsPressed = false;
|
xIsPressed = false;
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
|
// since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items
|
||||||
|
if (event.text == "`") {
|
||||||
|
handeMenuEvent("Edit Properties...");
|
||||||
|
}
|
||||||
|
if (event.text == "BACKSPACE") {
|
||||||
|
handeMenuEvent("Delete");
|
||||||
|
}
|
||||||
});
|
});
|
|
@ -3270,10 +3270,16 @@ void Application::nodeAdded(SharedNodePointer node) {
|
||||||
|
|
||||||
void Application::nodeKilled(SharedNodePointer node) {
|
void Application::nodeKilled(SharedNodePointer node) {
|
||||||
|
|
||||||
// this is here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
|
// These are here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
|
||||||
// OctreePacketProcessor::nodeKilled is not called when NodeList::nodeKilled is emitted for some reason.
|
// OctreePacketProcessor::nodeKilled is not being called when NodeList::nodeKilled is emitted.
|
||||||
|
// This may have to do with GenericThread::threadRoutine() blocking the QThread event loop
|
||||||
|
|
||||||
_octreeProcessor.nodeKilled(node);
|
_octreeProcessor.nodeKilled(node);
|
||||||
|
|
||||||
|
_voxelEditSender.nodeKilled(node);
|
||||||
|
_particleEditSender.nodeKilled(node);
|
||||||
|
_modelEditSender.nodeKilled(node);
|
||||||
|
|
||||||
if (node->getType() == NodeType::VoxelServer) {
|
if (node->getType() == NodeType::VoxelServer) {
|
||||||
QUuid nodeUUID = node->getUUID();
|
QUuid nodeUUID = node->getUUID();
|
||||||
// see if this is the first we've heard of this node...
|
// see if this is the first we've heard of this node...
|
||||||
|
|
|
@ -145,6 +145,15 @@ void DatagramProcessor::processDatagrams() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PacketTypeVoxelEditNack:
|
||||||
|
application->_voxelEditSender.processNackPacket(incomingPacket);
|
||||||
|
break;
|
||||||
|
case PacketTypeParticleEditNack:
|
||||||
|
application->_particleEditSender.processNackPacket(incomingPacket);
|
||||||
|
break;
|
||||||
|
case PacketTypeModelEditNack:
|
||||||
|
application->_modelEditSender.processNackPacket(incomingPacket);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -22,6 +22,17 @@ ModelTreeRenderer::ModelTreeRenderer() :
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelTreeRenderer::~ModelTreeRenderer() {
|
ModelTreeRenderer::~ModelTreeRenderer() {
|
||||||
|
clearModelsCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelTreeRenderer::clear() {
|
||||||
|
OctreeRenderer::clear();
|
||||||
|
clearModelsCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelTreeRenderer::clearModelsCache() {
|
||||||
|
qDebug() << "ModelTreeRenderer::clearModelsCache()...";
|
||||||
|
|
||||||
// delete the models in _knownModelsItemModels
|
// delete the models in _knownModelsItemModels
|
||||||
foreach(Model* model, _knownModelsItemModels) {
|
foreach(Model* model, _knownModelsItemModels) {
|
||||||
delete model;
|
delete model;
|
||||||
|
@ -71,24 +82,39 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) {
|
||||||
if (modelItem.isKnownID()) {
|
if (modelItem.isKnownID()) {
|
||||||
if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) {
|
if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) {
|
||||||
model = _knownModelsItemModels[modelItem.getID()];
|
model = _knownModelsItemModels[modelItem.getID()];
|
||||||
} else {
|
if (QUrl(modelItem.getModelURL()) != model->getURL()) {
|
||||||
|
delete model; // delete the old model...
|
||||||
|
model = NULL;
|
||||||
|
_knownModelsItemModels.remove(modelItem.getID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't have a model...
|
||||||
|
if (!model) {
|
||||||
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem));
|
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem));
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
model = new Model();
|
model = new Model();
|
||||||
model->init();
|
model->init();
|
||||||
model->setURL(QUrl(modelItem.getModelURL()));
|
model->setURL(QUrl(modelItem.getModelURL()));
|
||||||
_knownModelsItemModels[modelItem.getID()] = model;
|
_knownModelsItemModels[modelItem.getID()] = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) {
|
if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) {
|
||||||
model = _unknownModelsItemModels[modelItem.getCreatorTokenID()];
|
model = _unknownModelsItemModels[modelItem.getCreatorTokenID()];
|
||||||
} else {
|
if (QUrl(modelItem.getModelURL()) != model->getURL()) {
|
||||||
|
delete model; // delete the old model...
|
||||||
|
model = NULL;
|
||||||
|
_unknownModelsItemModels.remove(modelItem.getID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model) {
|
||||||
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
// Make sure we only create new models on the thread that owns the ModelTreeRenderer
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
|
||||||
|
|
|
@ -51,7 +51,11 @@ public:
|
||||||
|
|
||||||
virtual const FBXGeometry* getGeometryForModel(const ModelItem& modelItem);
|
virtual const FBXGeometry* getGeometryForModel(const ModelItem& modelItem);
|
||||||
|
|
||||||
|
/// clears the tree
|
||||||
|
virtual void clear();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void clearModelsCache();
|
||||||
Model* getModel(const ModelItem& modelItem);
|
Model* getModel(const ModelItem& modelItem);
|
||||||
QMap<uint32_t, Model*> _knownModelsItemModels;
|
QMap<uint32_t, Model*> _knownModelsItemModels;
|
||||||
QMap<uint32_t, Model*> _unknownModelsItemModels;
|
QMap<uint32_t, Model*> _unknownModelsItemModels;
|
||||||
|
|
|
@ -66,7 +66,10 @@ enum PacketType {
|
||||||
PacketTypeModelAddOrEdit,
|
PacketTypeModelAddOrEdit,
|
||||||
PacketTypeModelErase,
|
PacketTypeModelErase,
|
||||||
PacketTypeModelAddResponse,
|
PacketTypeModelAddResponse,
|
||||||
PacketTypeOctreeDataNack
|
PacketTypeOctreeDataNack, // 45
|
||||||
|
PacketTypeVoxelEditNack,
|
||||||
|
PacketTypeParticleEditNack,
|
||||||
|
PacketTypeModelEditNack,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef char PacketVersion;
|
typedef char PacketVersion;
|
||||||
|
@ -76,7 +79,7 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest
|
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest
|
||||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||||
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeModelQuery
|
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeModelQuery
|
||||||
<< PacketTypeOctreeDataNack;
|
<< PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeModelEditNack;
|
||||||
|
|
||||||
const int NUM_BYTES_MD5_HASH = 16;
|
const int NUM_BYTES_MD5_HASH = 16;
|
||||||
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
|
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
|
@ -35,9 +35,10 @@ bool ReceivedPacketProcessor::process() {
|
||||||
|
|
||||||
if (_packets.size() == 0) {
|
if (_packets.size() == 0) {
|
||||||
_waitingOnPacketsMutex.lock();
|
_waitingOnPacketsMutex.lock();
|
||||||
_hasPackets.wait(&_waitingOnPacketsMutex);
|
_hasPackets.wait(&_waitingOnPacketsMutex, getMaxWait());
|
||||||
_waitingOnPacketsMutex.unlock();
|
_waitingOnPacketsMutex.unlock();
|
||||||
}
|
}
|
||||||
|
preProcess();
|
||||||
while (_packets.size() > 0) {
|
while (_packets.size() > 0) {
|
||||||
lock(); // lock to make sure nothing changes on us
|
lock(); // lock to make sure nothing changes on us
|
||||||
NetworkPacket& packet = _packets.front(); // get the oldest packet
|
NetworkPacket& packet = _packets.front(); // get the oldest packet
|
||||||
|
@ -46,7 +47,9 @@ bool ReceivedPacketProcessor::process() {
|
||||||
_nodePacketCounts[temporary.getNode()->getUUID()]--;
|
_nodePacketCounts[temporary.getNode()->getUUID()]--;
|
||||||
unlock(); // let others add to the packets
|
unlock(); // let others add to the packets
|
||||||
processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy
|
processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy
|
||||||
|
midProcess();
|
||||||
}
|
}
|
||||||
|
postProcess();
|
||||||
return isStillRunning(); // keep running till they terminate us
|
return isStillRunning(); // keep running till they terminate us
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,19 @@ public:
|
||||||
/// Are there received packets waiting to be processed
|
/// Are there received packets waiting to be processed
|
||||||
bool hasPacketsToProcess() const { return _packets.size() > 0; }
|
bool hasPacketsToProcess() const { return _packets.size() > 0; }
|
||||||
|
|
||||||
/// Are there received packets waiting to be processed from a certain node
|
/// Is a specified node still alive?
|
||||||
|
bool isAlive(const QUuid& nodeUUID) const {
|
||||||
|
return _nodePacketCounts.contains(nodeUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Are there received packets waiting to be processed from a specified node
|
||||||
bool hasPacketsToProcessFrom(const SharedNodePointer& sendingNode) const {
|
bool hasPacketsToProcessFrom(const SharedNodePointer& sendingNode) const {
|
||||||
return _nodePacketCounts[sendingNode->getUUID()] > 0;
|
return hasPacketsToProcessFrom(sendingNode->getUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Are there received packets waiting to be processed from a specified node
|
||||||
|
bool hasPacketsToProcessFrom(const QUuid& nodeUUID) const {
|
||||||
|
return _nodePacketCounts[nodeUUID] > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How many received packets waiting are to be processed
|
/// How many received packets waiting are to be processed
|
||||||
|
@ -53,9 +63,21 @@ protected:
|
||||||
/// Implements generic processing behavior for this thread.
|
/// Implements generic processing behavior for this thread.
|
||||||
virtual bool process();
|
virtual bool process();
|
||||||
|
|
||||||
|
/// Determines the timeout of the wait when there are no packets to process. Default value means no timeout
|
||||||
|
virtual unsigned long getMaxWait() const { return ULONG_MAX; }
|
||||||
|
|
||||||
|
/// Override to do work before the packets processing loop. Default does nothing.
|
||||||
|
virtual void preProcess() { }
|
||||||
|
|
||||||
|
/// Override to do work inside the packet processing loop after a packet is processed. Default does nothing.
|
||||||
|
virtual void midProcess() { }
|
||||||
|
|
||||||
|
/// Override to do work after the packets processing loop. Default does nothing.
|
||||||
|
virtual void postProcess() { }
|
||||||
|
|
||||||
virtual void terminating();
|
virtual void terminating();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
QVector<NetworkPacket> _packets;
|
QVector<NetworkPacket> _packets;
|
||||||
QHash<QUuid, int> _nodePacketCounts;
|
QHash<QUuid, int> _nodePacketCounts;
|
||||||
|
|
63
libraries/networking/src/SentPacketHistory.cpp
Normal file
63
libraries/networking/src/SentPacketHistory.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
//
|
||||||
|
// SentPacketHistory.cpp
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// Created by Yixin Wang on 6/5/2014
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include "SentPacketHistory.h"
|
||||||
|
#include <qdebug.h>
|
||||||
|
|
||||||
|
SentPacketHistory::SentPacketHistory(int size)
|
||||||
|
: _sentPackets(size),
|
||||||
|
_newestPacketAt(0),
|
||||||
|
_numExistingPackets(0),
|
||||||
|
_newestSequenceNumber(std::numeric_limits<uint16_t>::max())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SentPacketHistory::packetSent(uint16_t sequenceNumber, const QByteArray& packet) {
|
||||||
|
|
||||||
|
// check if given seq number has the expected value. if not, something's wrong with
|
||||||
|
// the code calling this function
|
||||||
|
uint16_t expectedSequenceNumber = _newestSequenceNumber + (uint16_t)1;
|
||||||
|
if (sequenceNumber != expectedSequenceNumber) {
|
||||||
|
qDebug() << "Unexpected sequence number passed to SentPacketHistory::packetSent()!"
|
||||||
|
<< "Expected:" << expectedSequenceNumber << "Actual:" << sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
_newestSequenceNumber = sequenceNumber;
|
||||||
|
|
||||||
|
// increment _newestPacketAt cyclically, insert new packet there.
|
||||||
|
// this will overwrite the oldest packet in the buffer
|
||||||
|
_newestPacketAt = (_newestPacketAt == _sentPackets.size() - 1) ? 0 : _newestPacketAt + 1;
|
||||||
|
_sentPackets[_newestPacketAt] = packet;
|
||||||
|
if (_numExistingPackets < _sentPackets.size()) {
|
||||||
|
_numExistingPackets++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray* SentPacketHistory::getPacket(uint16_t sequenceNumber) const {
|
||||||
|
|
||||||
|
const int UINT16_RANGE = std::numeric_limits<uint16_t>::max() + 1;
|
||||||
|
|
||||||
|
// if sequenceNumber > _newestSequenceNumber, assume sequenceNumber is from before the most recent rollover
|
||||||
|
// correct the diff so that it correctly represents how far back in the history sequenceNumber is
|
||||||
|
int seqDiff = (int)_newestSequenceNumber - (int)sequenceNumber;
|
||||||
|
if (seqDiff < 0) {
|
||||||
|
seqDiff += UINT16_RANGE;
|
||||||
|
}
|
||||||
|
// if desired sequence number is too old to be found in the history, return null
|
||||||
|
if (seqDiff >= _numExistingPackets) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int packetAt = _newestPacketAt - seqDiff;
|
||||||
|
if (packetAt < 0) {
|
||||||
|
packetAt += _sentPackets.size();
|
||||||
|
}
|
||||||
|
return &_sentPackets.at(packetAt);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// SentPacketHistory.h
|
// SentPacketHistory.h
|
||||||
// assignement-client/src/octree
|
// libraries/networking/src
|
||||||
//
|
//
|
||||||
// Created by Yixin Wang on 6/5/2014
|
// Created by Yixin Wang on 6/5/2014
|
||||||
//
|
//
|
||||||
|
@ -11,25 +11,24 @@
|
||||||
#ifndef hifi_SentPacketHistory_h
|
#ifndef hifi_SentPacketHistory_h
|
||||||
#define hifi_SentPacketHistory_h
|
#define hifi_SentPacketHistory_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <qbytearray.h>
|
#include <qbytearray.h>
|
||||||
#include <qvector.h>
|
#include <qvector.h>
|
||||||
|
|
||||||
#include "OctreePacketData.h"
|
|
||||||
|
|
||||||
class SentPacketHistory {
|
class SentPacketHistory {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SentPacketHistory(int size);
|
SentPacketHistory(int size = 1000);
|
||||||
|
|
||||||
void packetSent(OCTREE_PACKET_SEQUENCE sequenceNumber, const QByteArray& packet);
|
void packetSent(uint16_t sequenceNumber, const QByteArray& packet);
|
||||||
const QByteArray* getPacket(OCTREE_PACKET_SEQUENCE sequenceNumber) const;
|
const QByteArray* getPacket(uint16_t sequenceNumber) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<QByteArray> _sentPackets; // circular buffer
|
QVector<QByteArray> _sentPackets; // circular buffer
|
||||||
int _newestPacketAt;
|
int _newestPacketAt;
|
||||||
int _numExistingPackets;
|
int _numExistingPackets;
|
||||||
|
|
||||||
OCTREE_PACKET_SEQUENCE _newestSequenceNumber;
|
uint16_t _newestSequenceNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -17,7 +17,6 @@
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include "OctreeEditPacketSender.h"
|
#include "OctreeEditPacketSender.h"
|
||||||
|
|
||||||
|
|
||||||
EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) :
|
EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) :
|
||||||
_nodeUUID(nodeUUID),
|
_nodeUUID(nodeUUID),
|
||||||
_currentType(type),
|
_currentType(type),
|
||||||
|
@ -89,7 +88,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
|
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
|
||||||
// a known nodeID.
|
// a known nodeID.
|
||||||
void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, ssize_t length) {
|
void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, const unsigned char* buffer, ssize_t length) {
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||||
|
@ -97,12 +96,19 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c
|
||||||
if (node->getType() == getMyNodeType() &&
|
if (node->getType() == getMyNodeType() &&
|
||||||
((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
|
((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
|
||||||
if (node->getActiveSocket()) {
|
if (node->getActiveSocket()) {
|
||||||
queuePacketForSending(node, QByteArray(reinterpret_cast<char*>(buffer), length));
|
QByteArray packet(reinterpret_cast<const char*>(buffer), length);
|
||||||
|
queuePacketForSending(node, packet);
|
||||||
|
|
||||||
|
// extract sequence number and add packet to history
|
||||||
|
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||||
|
const char* dataAt = reinterpret_cast<const char*>(packet.data()) + numBytesPacketHeader;
|
||||||
|
unsigned short int sequence = *((unsigned short int*)dataAt);
|
||||||
|
_sentPacketHistories[nodeUUID].packetSent(sequence, packet);
|
||||||
|
|
||||||
// debugging output...
|
// debugging output...
|
||||||
bool wantDebugging = false;
|
bool wantDebugging = false;
|
||||||
if (wantDebugging) {
|
if (wantDebugging) {
|
||||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<char*>(buffer));
|
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(buffer));
|
||||||
unsigned short int sequence = (*((unsigned short int*)(buffer + numBytesPacketHeader)));
|
unsigned short int sequence = (*((unsigned short int*)(buffer + numBytesPacketHeader)));
|
||||||
quint64 createdAt = (*((quint64*)(buffer + numBytesPacketHeader + sizeof(sequence))));
|
quint64 createdAt = (*((quint64*)(buffer + numBytesPacketHeader + sizeof(sequence))));
|
||||||
quint64 queuedAt = usecTimestampNow();
|
quint64 queuedAt = usecTimestampNow();
|
||||||
|
@ -287,18 +293,20 @@ void OctreeEditPacketSender::releaseQueuedMessages() {
|
||||||
if (!serversExist()) {
|
if (!serversExist()) {
|
||||||
_releaseQueuedMessagesPending = true;
|
_releaseQueuedMessagesPending = true;
|
||||||
} else {
|
} else {
|
||||||
for (std::map<QUuid, EditPacketBuffer>::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) {
|
for (QHash<QUuid, EditPacketBuffer>::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) {
|
||||||
releaseQueuedPacket(i->second);
|
releaseQueuedPacket(i.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) {
|
void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) {
|
||||||
|
_releaseQueuedPacketMutex.lock();
|
||||||
if (packetBuffer._currentSize > 0 && packetBuffer._currentType != PacketTypeUnknown) {
|
if (packetBuffer._currentSize > 0 && packetBuffer._currentType != PacketTypeUnknown) {
|
||||||
queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize);
|
queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize);
|
||||||
|
packetBuffer._currentSize = 0;
|
||||||
|
packetBuffer._currentType = PacketTypeUnknown;
|
||||||
}
|
}
|
||||||
packetBuffer._currentSize = 0;
|
_releaseQueuedPacketMutex.unlock();
|
||||||
packetBuffer._currentType = PacketTypeUnknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type) {
|
void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type) {
|
||||||
|
@ -329,3 +337,41 @@ bool OctreeEditPacketSender::process() {
|
||||||
// base class does most of the work.
|
// base class does most of the work.
|
||||||
return PacketSender::process();
|
return PacketSender::process();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OctreeEditPacketSender::processNackPacket(const QByteArray& packet) {
|
||||||
|
// parse sending node from packet, retrieve packet history for that node
|
||||||
|
QUuid sendingNodeUUID = uuidFromPacketHeader(packet);
|
||||||
|
|
||||||
|
// if packet history doesn't exist for the sender node (somehow), bail
|
||||||
|
if (!_sentPacketHistories.contains(sendingNodeUUID)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const SentPacketHistory& sentPacketHistory = _sentPacketHistories.value(sendingNodeUUID);
|
||||||
|
|
||||||
|
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||||
|
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data()) + numBytesPacketHeader;
|
||||||
|
|
||||||
|
// read number of sequence numbers
|
||||||
|
uint16_t numSequenceNumbers = (*(uint16_t*)dataAt);
|
||||||
|
dataAt += sizeof(uint16_t);
|
||||||
|
|
||||||
|
// read sequence numbers and queue packets for resend
|
||||||
|
for (int i = 0; i < numSequenceNumbers; i++) {
|
||||||
|
unsigned short int sequenceNumber = (*(unsigned short int*)dataAt);
|
||||||
|
dataAt += sizeof(unsigned short int);
|
||||||
|
|
||||||
|
// retrieve packet from history
|
||||||
|
const QByteArray* packet = sentPacketHistory.getPacket(sequenceNumber);
|
||||||
|
if (packet) {
|
||||||
|
const SharedNodePointer& node = NodeList::getInstance()->getNodeHash().value(sendingNodeUUID);
|
||||||
|
queuePacketForSending(node, *packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeEditPacketSender::nodeKilled(SharedNodePointer node) {
|
||||||
|
// TODO: add locks
|
||||||
|
QUuid nodeUUID = node->getUUID();
|
||||||
|
_pendingEditPackets.remove(nodeUUID);
|
||||||
|
_sentPacketHistories.remove(nodeUUID);
|
||||||
|
}
|
||||||
|
|
|
@ -12,9 +12,11 @@
|
||||||
#ifndef hifi_OctreeEditPacketSender_h
|
#ifndef hifi_OctreeEditPacketSender_h
|
||||||
#define hifi_OctreeEditPacketSender_h
|
#define hifi_OctreeEditPacketSender_h
|
||||||
|
|
||||||
|
#include <qqueue.h>
|
||||||
#include <PacketSender.h>
|
#include <PacketSender.h>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include "JurisdictionMap.h"
|
#include "JurisdictionMap.h"
|
||||||
|
#include "SentPacketHistory.h"
|
||||||
|
|
||||||
/// Used for construction of edit packets
|
/// Used for construction of edit packets
|
||||||
class EditPacketBuffer {
|
class EditPacketBuffer {
|
||||||
|
@ -89,10 +91,16 @@ public:
|
||||||
// you must override these...
|
// you must override these...
|
||||||
virtual char getMyNodeType() const = 0;
|
virtual char getMyNodeType() const = 0;
|
||||||
virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { };
|
virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { };
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void nodeKilled(SharedNodePointer node);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void processNackPacket(const QByteArray& packet);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _shouldSend;
|
bool _shouldSend;
|
||||||
void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length);
|
void queuePacketToNode(const QUuid& nodeID, const unsigned char* buffer, ssize_t length);
|
||||||
void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length);
|
void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length);
|
||||||
void queuePacketToNodes(unsigned char* buffer, ssize_t length);
|
void queuePacketToNodes(unsigned char* buffer, ssize_t length);
|
||||||
void initializePacket(EditPacketBuffer& packetBuffer, PacketType type);
|
void initializePacket(EditPacketBuffer& packetBuffer, PacketType type);
|
||||||
|
@ -101,7 +109,7 @@ protected:
|
||||||
void processPreServerExistsPackets();
|
void processPreServerExistsPackets();
|
||||||
|
|
||||||
// These are packets which are destined from know servers but haven't been released because they're still too small
|
// These are packets which are destined from know servers but haven't been released because they're still too small
|
||||||
std::map<QUuid, EditPacketBuffer> _pendingEditPackets;
|
QHash<QUuid, EditPacketBuffer> _pendingEditPackets;
|
||||||
|
|
||||||
// These are packets that are waiting to be processed because we don't yet know if there are servers
|
// These are packets that are waiting to be processed because we don't yet know if there are servers
|
||||||
int _maxPendingMessages;
|
int _maxPendingMessages;
|
||||||
|
@ -114,5 +122,10 @@ protected:
|
||||||
|
|
||||||
unsigned short int _sequenceNumber;
|
unsigned short int _sequenceNumber;
|
||||||
int _maxPacketSize;
|
int _maxPacketSize;
|
||||||
|
|
||||||
|
QMutex _releaseQueuedPacketMutex;
|
||||||
|
|
||||||
|
// TODO: add locks for this and _pendingEditPackets
|
||||||
|
QHash<QUuid, SentPacketHistory> _sentPacketHistories;
|
||||||
};
|
};
|
||||||
#endif // hifi_OctreeEditPacketSender_h
|
#endif // hifi_OctreeEditPacketSender_h
|
||||||
|
|
|
@ -62,7 +62,7 @@ public:
|
||||||
static bool renderOperation(OctreeElement* element, void* extraData);
|
static bool renderOperation(OctreeElement* element, void* extraData);
|
||||||
|
|
||||||
/// clears the tree
|
/// clears the tree
|
||||||
void clear();
|
virtual void clear();
|
||||||
protected:
|
protected:
|
||||||
Octree* _tree;
|
Octree* _tree;
|
||||||
bool _managedTree;
|
bool _managedTree;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
@ -842,7 +843,7 @@ const char* OctreeSceneStats::getItemValue(Item item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
||||||
bool wasStatsPacket, int nodeClockSkewUsec) {
|
bool wasStatsPacket, int nodeClockSkewUsec) {
|
||||||
const bool wantExtraDebugging = false;
|
const bool wantExtraDebugging = false;
|
||||||
|
|
||||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||||
|
@ -852,10 +853,10 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
||||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||||
|
|
||||||
OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||||
|
|
||||||
//bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT);
|
//bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT);
|
||||||
//bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
|
//bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
|
||||||
|
|
||||||
|
@ -877,29 +878,15 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
||||||
return; // ignore any packets that are unreasonable
|
return; // ignore any packets that are unreasonable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int UINT16_RANGE = std::numeric_limits<uint16_t>::max() + 1;
|
||||||
|
|
||||||
// determine our expected sequence number... handle rollover appropriately
|
// determine our expected sequence number... handle rollover appropriately
|
||||||
OCTREE_PACKET_SEQUENCE expected = _incomingPacket > 0 ? _incomingLastSequence + 1 : sequence;
|
OCTREE_PACKET_SEQUENCE expected = _incomingPacket > 0 ? _incomingLastSequence + (quint16)1 : sequence;
|
||||||
|
|
||||||
// Guard against possible corrupted packets... with bad sequence numbers
|
|
||||||
const int MAX_RESONABLE_SEQUENCE_OFFSET = 2000;
|
|
||||||
const int MIN_RESONABLE_SEQUENCE_OFFSET = -2000;
|
|
||||||
int sequenceOffset = (sequence - expected);
|
|
||||||
if (sequenceOffset > MAX_RESONABLE_SEQUENCE_OFFSET || sequenceOffset < MIN_RESONABLE_SEQUENCE_OFFSET) {
|
|
||||||
qDebug() << "ignoring unreasonable packet... sequence:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
|
|
||||||
return; // ignore any packets that are unreasonable
|
|
||||||
}
|
|
||||||
|
|
||||||
// track packets here...
|
|
||||||
_incomingPacket++;
|
|
||||||
_incomingBytes += packet.size();
|
|
||||||
if (!wasStatsPacket) {
|
|
||||||
_incomingWastedBytes += (MAX_PACKET_SIZE - packet.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
const int USECS_PER_MSEC = 1000;
|
const int USECS_PER_MSEC = 1000;
|
||||||
float flightTimeMsecs = flightTime / USECS_PER_MSEC;
|
float flightTimeMsecs = flightTime / USECS_PER_MSEC;
|
||||||
_incomingFlightTimeAverage.updateAverage(flightTimeMsecs);
|
_incomingFlightTimeAverage.updateAverage(flightTimeMsecs);
|
||||||
|
|
||||||
// track out of order and possibly lost packets...
|
// track out of order and possibly lost packets...
|
||||||
if (sequence == _incomingLastSequence) {
|
if (sequence == _incomingLastSequence) {
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
|
@ -911,15 +898,46 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
||||||
qDebug() << "out of order... got:" << sequence << "expected:" << expected;
|
qDebug() << "out of order... got:" << sequence << "expected:" << expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sequenceInt = (int)sequence;
|
||||||
|
int expectedInt = (int)expected;
|
||||||
|
|
||||||
|
// if distance between sequence and expected are more than half of the total range of possible seq numbers,
|
||||||
|
// assume that a rollover occurred between the two.
|
||||||
|
// correct the larger one so it's in the range [-UINT16_RANGE, -1] while the other remains in [0, UINT16_RANGE-1]
|
||||||
|
// after doing so, sequenceInt and expectedInt can be correctly compared to each other, though one may be negative
|
||||||
|
if (std::abs(sequenceInt - expectedInt) > UINT16_RANGE / 2) {
|
||||||
|
if (sequenceInt > expectedInt) {
|
||||||
|
sequenceInt -= UINT16_RANGE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expectedInt -= UINT16_RANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guard against possible corrupted packets... with bad sequence numbers
|
||||||
|
const int MAX_RESONABLE_SEQUENCE_OFFSET = 2000;
|
||||||
|
const int MIN_RESONABLE_SEQUENCE_OFFSET = -2000;
|
||||||
|
|
||||||
|
int sequenceOffset = (sequenceInt - expectedInt);
|
||||||
|
if (sequenceOffset > MAX_RESONABLE_SEQUENCE_OFFSET || sequenceOffset < MIN_RESONABLE_SEQUENCE_OFFSET) {
|
||||||
|
qDebug() << "ignoring unreasonable packet... sequence:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
|
||||||
|
return; // ignore any packets that are unreasonable
|
||||||
|
}
|
||||||
|
|
||||||
// if the sequence is less than our expected, then this might be a packet
|
// if the sequence is less than our expected, then this might be a packet
|
||||||
// that was delayed and so we should find it in our lostSequence list
|
// that was delayed and so we should find it in our lostSequence list
|
||||||
if (sequence < expected) {
|
if (sequenceInt < expectedInt) {
|
||||||
|
|
||||||
|
// if no rollover between them: sequenceInt, expectedInt are both in range [0, UINT16_RANGE-1]
|
||||||
|
// if rollover between them: sequenceInt in [-UINT16_RANGE, -1], expectedInt in [0, UINT16_RANGE-1]
|
||||||
|
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
qDebug() << "this packet is later than expected...";
|
qDebug() << "this packet is later than expected...";
|
||||||
}
|
}
|
||||||
if (sequence < std::max(0, (expected - MAX_MISSING_SEQUENCE_OLD_AGE))) {
|
if (sequenceInt < expectedInt - MAX_MISSING_SEQUENCE_OLD_AGE) {
|
||||||
_incomingReallyLate++;
|
_incomingReallyLate++;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
_incomingLate++;
|
_incomingLate++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -931,57 +949,78 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
||||||
_sequenceNumbersToNack.remove(sequence);
|
_sequenceNumbersToNack.remove(sequence);
|
||||||
_incomingLikelyLost--;
|
_incomingLikelyLost--;
|
||||||
_incomingRecovered++;
|
_incomingRecovered++;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// if we're still in our pruning window, and we didn't find it in our missing list,
|
// if we're still in our pruning window, and we didn't find it in our missing list,
|
||||||
// than this is really unexpected and can probably only happen if the packet was a
|
// than this is really unexpected and can probably only happen if the packet was a
|
||||||
// duplicate
|
// duplicate
|
||||||
if (sequence >= std::max(0, (expected - MAX_MISSING_SEQUENCE_OLD_AGE))) {
|
if (sequenceInt >= expectedInt - MAX_MISSING_SEQUENCE_OLD_AGE) {
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
qDebug() << "sequence:" << sequence << "WAS NOT found in _missingSequenceNumbers, and not that old... (expected - MAX_MISSING_SEQUENCE_OLD_AGE):" << (expected - MAX_MISSING_SEQUENCE_OLD_AGE);
|
qDebug() << "sequence:" << sequence << "WAS NOT found in _missingSequenceNumbers, and not that old... (expected - MAX_MISSING_SEQUENCE_OLD_AGE):"
|
||||||
|
<< (uint16_t)(expectedInt - MAX_MISSING_SEQUENCE_OLD_AGE);
|
||||||
}
|
}
|
||||||
_incomingPossibleDuplicate++;
|
_incomingPossibleDuplicate++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// don't update _incomingLastSequence in this case.
|
||||||
if (sequence > expected) {
|
// only bump the last sequence if it was greater than our expected sequence, this will keep us from
|
||||||
|
// accidentally going backwards when an out of order (recovered) packet comes in
|
||||||
|
|
||||||
|
} else { // sequenceInt > expectedInt
|
||||||
|
|
||||||
|
// if no rollover between them: sequenceInt, expectedInt are both in range [0, UINT16_RANGE-1]
|
||||||
|
// if rollover between them: sequenceInt in [0, UINT16_RANGE-1], expectedInt in [-UINT16_RANGE, -1]
|
||||||
|
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
qDebug() << "this packet is earlier than expected...";
|
qDebug() << "this packet is earlier than expected...";
|
||||||
}
|
}
|
||||||
_incomingEarly++;
|
_incomingEarly++;
|
||||||
|
|
||||||
// hmm... so, we either didn't get some packets, or this guy came early...
|
// hmm... so, we either didn't get some packets, or this guy came early...
|
||||||
unsigned int missing = sequence - expected;
|
int missing = sequenceInt - expectedInt;
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
qDebug() << ">>>>>>>> missing gap=" << missing;
|
qDebug() << ">>>>>>>> missing gap=" << missing;
|
||||||
}
|
}
|
||||||
_incomingLikelyLost += missing;
|
_incomingLikelyLost += missing;
|
||||||
for(unsigned int missingSequence = expected; missingSequence < sequence; missingSequence++) {
|
for (int missingSequenceInt = expectedInt; missingSequenceInt < sequenceInt; missingSequenceInt++) {
|
||||||
|
OCTREE_PACKET_SEQUENCE missingSequence = missingSequenceInt >= 0 ? missingSequenceInt : missingSequenceInt + UINT16_RANGE;
|
||||||
_missingSequenceNumbers << missingSequence;
|
_missingSequenceNumbers << missingSequence;
|
||||||
_sequenceNumbersToNack << missingSequence;
|
_sequenceNumbersToNack << missingSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_incomingLastSequence = sequence;
|
||||||
}
|
}
|
||||||
|
} else { // sequence = expected
|
||||||
|
|
||||||
|
_incomingLastSequence = sequence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// only bump the last sequence if it was greater than our expected sequence, this will keep us from
|
|
||||||
// accidentally going backwards when an out of order (recovered) packet comes in
|
|
||||||
if (sequence >= expected) {
|
|
||||||
_incomingLastSequence = sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do some garbage collecting on our _missingSequenceNumbers
|
// do some garbage collecting on our _missingSequenceNumbers
|
||||||
if (_missingSequenceNumbers.size() > MAX_MISSING_SEQUENCE) {
|
if (_missingSequenceNumbers.size() > MAX_MISSING_SEQUENCE) {
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
qDebug() << "too many _missingSequenceNumbers:" << _missingSequenceNumbers.size();
|
qDebug() << "too many _missingSequenceNumbers:" << _missingSequenceNumbers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int oldAgeCutoff = (int)_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE;
|
||||||
|
|
||||||
foreach(uint16_t missingItem, _missingSequenceNumbers) {
|
foreach(uint16_t missingItem, _missingSequenceNumbers) {
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
qDebug() << "checking item:" << missingItem << "is it in need of pruning?";
|
qDebug() << "checking item:" << missingItem << "is it in need of pruning?";
|
||||||
qDebug() << "(_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE):"
|
qDebug() << "(_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE):"
|
||||||
<< (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE);
|
<< (uint16_t)((int)_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE);
|
||||||
}
|
}
|
||||||
if (missingItem <= std::max(0, _incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE)) {
|
|
||||||
|
bool prune;
|
||||||
|
if (oldAgeCutoff >= 0) {
|
||||||
|
prune = (missingItem <= oldAgeCutoff || missingItem > _incomingLastSequence);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prune = (missingItem <= oldAgeCutoff + UINT16_RANGE && missingItem > _incomingLastSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prune) {
|
||||||
if (wantExtraDebugging) {
|
if (wantExtraDebugging) {
|
||||||
qDebug() << "pruning really old missing sequence:" << missingItem;
|
qDebug() << "pruning really old missing sequence:" << missingItem;
|
||||||
}
|
}
|
||||||
|
@ -991,6 +1030,12 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// track packets here...
|
||||||
|
_incomingPacket++;
|
||||||
|
_incomingBytes += packet.size();
|
||||||
|
if (!wasStatsPacket) {
|
||||||
|
_incomingWastedBytes += (MAX_PACKET_SIZE - packet.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int OctreeSceneStats::getNumSequenceNumbersToNack() const {
|
int OctreeSceneStats::getNumSequenceNumbersToNack() const {
|
||||||
|
|
|
@ -57,7 +57,7 @@ protected:
|
||||||
|
|
||||||
bool isStillRunning() const { return !_stopThread; }
|
bool isStillRunning() const { return !_stopThread; }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
QMutex _mutex;
|
QMutex _mutex;
|
||||||
|
|
||||||
bool _stopThread;
|
bool _stopThread;
|
||||||
|
|
Loading…
Reference in a new issue