Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels

This commit is contained in:
Andrzej Kapolka 2014-06-18 11:59:04 -07:00
commit 49ab3d6b43
23 changed files with 587 additions and 151 deletions

View file

@ -33,6 +33,7 @@ public:
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
virtual PacketType getMyEditNackType() const { return PacketTypeModelEditNack; }
// subclass may implement these method
virtual void beforeRun();

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <limits>
#include <PacketHeaders.h>
#include <PerfStat.h>
@ -17,6 +18,7 @@
#include "OctreeInboundPacketProcessor.h"
static QUuid DEFAULT_NODE_ID_REF;
const quint64 TOO_LONG_SINCE_LAST_NACK = 1 * USECS_PER_SECOND;
OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServer) :
_myServer(myServer),
@ -25,7 +27,8 @@ OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServe
_totalProcessTime(0),
_totalLockWaitTime(0),
_totalElementsInPacket(0),
_totalPackets(0)
_totalPackets(0),
_lastNackTime(usecTimestampNow())
{
}
@ -35,10 +38,38 @@ void OctreeInboundPacketProcessor::resetStats() {
_totalLockWaitTime = 0;
_totalElementsInPacket = 0;
_totalPackets = 0;
_lastNackTime = usecTimestampNow();
_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) {
@ -123,13 +154,13 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin
qDebug() << "sender has no known nodeUUID.";
}
}
trackInboundPackets(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
} else {
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) {
_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...
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
SingleSenderStats stats;
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalElementsInPacket += editsInPacket;
stats._totalPackets++;
stats.trackInboundPacket(sequence, transitTime, editsInPacket, processTime, lockWaitTime);
_singleSenderStats[nodeUUID] = stats;
} else {
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalElementsInPacket += editsInPacket;
stats._totalPackets++;
stats.trackInboundPacket(sequence, transitTime, editsInPacket, processTime, lockWaitTime);
}
}
int OctreeInboundPacketProcessor::sendNackPackets() {
SingleSenderStats::SingleSenderStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalElementsInPacket = 0;
_totalPackets = 0;
int packetsSent = 0;
NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
while (i != _singleSenderStats.end()) {
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++;
}

View file

@ -32,16 +32,24 @@ public:
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
quint64 getAverageLockWaitTimePerElement() const
{ 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 _totalProcessTime;
quint64 _totalLockWaitTime;
quint64 _totalElementsInPacket;
quint64 _totalPackets;
unsigned short int _incomingLastSequence;
QSet<unsigned short int> _missingSequenceNumbers;
};
typedef std::map<QUuid, SingleSenderStats> NodeToSenderStatsMap;
typedef std::map<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterator;
typedef QHash<QUuid, SingleSenderStats> NodeToSenderStatsMap;
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
@ -66,10 +74,18 @@ public:
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
protected:
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
virtual unsigned long getMaxWait() const;
virtual void preProcess();
virtual void midProcess();
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);
OctreeServer* _myServer;
@ -82,5 +98,7 @@ private:
quint64 _totalPackets;
NodeToSenderStatsMap _singleSenderStats;
quint64 _lastNackTime;
};
#endif // hifi_OctreeInboundPacketProcessor_h

View file

@ -648,10 +648,10 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
int senderNumber = 0;
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
for (NodeToSenderStatsMapConstIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
senderNumber++;
QUuid senderID = i->first;
SingleSenderStats& senderStats = i->second;
QUuid senderID = i.key();
const SingleSenderStats& senderStats = i.value();
statsString += QString("\r\n Stats for sender %1 uuid: %2\r\n")
.arg(senderNumber).arg(senderID.toString());
@ -1060,6 +1060,9 @@ void OctreeServer::nodeAdded(SharedNodePointer node) {
void OctreeServer::nodeKilled(SharedNodePointer node) {
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;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
if (nodeData) {

View file

@ -68,6 +68,7 @@ public:
virtual const char* getMyServerName() const = 0;
virtual const char* getMyLoggingServerTargetName() const = 0;
virtual const char* getMyDefaultPersistFilename() const = 0;
virtual PacketType getMyEditNackType() const = 0;
// subclass may implement these method
virtual void beforeRun() { };

View file

@ -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);
}

View file

@ -33,6 +33,7 @@ public:
virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; }
virtual PacketType getMyEditNackType() const { return PacketTypeParticleEditNack; }
// subclass may implement these method
virtual void beforeRun();

View file

@ -42,6 +42,7 @@ public:
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; }
virtual PacketType getMyEditNackType() const { return PacketTypeVoxelEditNack; }
// subclass may implement these method
virtual void beforeRun();

View file

@ -717,8 +717,8 @@ function rayPlaneIntersection(pickRay, point, normal) {
function Tooltip() {
this.x = 285;
this.y = 115;
this.width = 110;
this.height = 115 ;
this.width = 500;
this.height = 145 ;
this.margin = 5;
this.decimals = 3;
@ -746,6 +746,9 @@ function Tooltip() {
text += "yaw: " + angles.y.toFixed(this.decimals) + "\n"
text += "roll: " + angles.z.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 });
}
@ -1019,9 +1022,11 @@ var modelMenuAddedDelete = false;
function setupModelMenus() {
print("setupModelMenus()");
// 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")) {
print("no delete... adding ours");
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" });
modelMenuAddedDelete = true;
@ -1031,9 +1036,10 @@ function setupModelMenus() {
}
function cleanupModelMenus() {
Menu.removeSeparator("Edit", "Models");
Menu.removeMenuItem("Edit", "Edit Properties...");
if (modelMenuAddedDelete) {
// delete our menuitems
Menu.removeSeparator("Edit", "Models");
Menu.removeMenuItem("Edit", "Delete");
}
}
@ -1054,7 +1060,8 @@ Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
setupModelMenus();
Menu.menuItemEvent.connect(function(menuItem){
function handeMenuEvent(menuItem){
print("menuItemEvent() in JS... menuItem=" + menuItem);
if (menuItem == "Delete") {
if (leftController.grabbing) {
@ -1072,8 +1079,36 @@ Menu.menuItemEvent.connect(function(menuItem){
} else {
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
@ -1107,4 +1142,11 @@ Controller.keyReleaseEvent.connect(function(event) {
xIsPressed = false;
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");
}
});

View file

@ -3270,10 +3270,16 @@ void Application::nodeAdded(SharedNodePointer node) {
void Application::nodeKilled(SharedNodePointer node) {
// this is here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
// OctreePacketProcessor::nodeKilled is not called when NodeList::nodeKilled is emitted for some reason.
// These are here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
// 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);
_voxelEditSender.nodeKilled(node);
_particleEditSender.nodeKilled(node);
_modelEditSender.nodeKilled(node);
if (node->getType() == NodeType::VoxelServer) {
QUuid nodeUUID = node->getUUID();
// see if this is the first we've heard of this node...

View file

@ -145,6 +145,15 @@ void DatagramProcessor::processDatagrams() {
}
break;
}
case PacketTypeVoxelEditNack:
application->_voxelEditSender.processNackPacket(incomingPacket);
break;
case PacketTypeParticleEditNack:
application->_particleEditSender.processNackPacket(incomingPacket);
break;
case PacketTypeModelEditNack:
application->_modelEditSender.processNackPacket(incomingPacket);
break;
default:
nodeList->processNodeData(senderSockAddr, incomingPacket);
break;

View file

@ -22,6 +22,17 @@ ModelTreeRenderer::ModelTreeRenderer() :
}
ModelTreeRenderer::~ModelTreeRenderer() {
clearModelsCache();
}
void ModelTreeRenderer::clear() {
OctreeRenderer::clear();
clearModelsCache();
}
void ModelTreeRenderer::clearModelsCache() {
qDebug() << "ModelTreeRenderer::clearModelsCache()...";
// delete the models in _knownModelsItemModels
foreach(Model* model, _knownModelsItemModels) {
delete model;
@ -71,24 +82,39 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) {
if (modelItem.isKnownID()) {
if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) {
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
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem));
return model;
}
model = new Model();
model->init();
model->setURL(QUrl(modelItem.getModelURL()));
_knownModelsItemModels[modelItem.getID()] = model;
}
} else {
if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) {
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
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection,

View file

@ -51,7 +51,11 @@ public:
virtual const FBXGeometry* getGeometryForModel(const ModelItem& modelItem);
/// clears the tree
virtual void clear();
protected:
void clearModelsCache();
Model* getModel(const ModelItem& modelItem);
QMap<uint32_t, Model*> _knownModelsItemModels;
QMap<uint32_t, Model*> _unknownModelsItemModels;

View file

@ -66,7 +66,10 @@ enum PacketType {
PacketTypeModelAddOrEdit,
PacketTypeModelErase,
PacketTypeModelAddResponse,
PacketTypeOctreeDataNack
PacketTypeOctreeDataNack, // 45
PacketTypeVoxelEditNack,
PacketTypeParticleEditNack,
PacketTypeModelEditNack,
};
typedef char PacketVersion;
@ -76,7 +79,7 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeModelQuery
<< PacketTypeOctreeDataNack;
<< PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeModelEditNack;
const int NUM_BYTES_MD5_HASH = 16;
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;

View file

@ -35,9 +35,10 @@ bool ReceivedPacketProcessor::process() {
if (_packets.size() == 0) {
_waitingOnPacketsMutex.lock();
_hasPackets.wait(&_waitingOnPacketsMutex);
_hasPackets.wait(&_waitingOnPacketsMutex, getMaxWait());
_waitingOnPacketsMutex.unlock();
}
preProcess();
while (_packets.size() > 0) {
lock(); // lock to make sure nothing changes on us
NetworkPacket& packet = _packets.front(); // get the oldest packet
@ -46,7 +47,9 @@ bool ReceivedPacketProcessor::process() {
_nodePacketCounts[temporary.getNode()->getUUID()]--;
unlock(); // let others add to the packets
processPacket(temporary.getNode(), temporary.getByteArray()); // process our temporary copy
midProcess();
}
postProcess();
return isStillRunning(); // keep running till they terminate us
}

View file

@ -33,9 +33,19 @@ public:
/// Are there received packets waiting to be processed
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 {
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
@ -53,9 +63,21 @@ protected:
/// Implements generic processing behavior for this thread.
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();
private:
protected:
QVector<NetworkPacket> _packets;
QHash<QUuid, int> _nodePacketCounts;

View 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);
}

View file

@ -1,6 +1,6 @@
//
// SentPacketHistory.h
// assignement-client/src/octree
// libraries/networking/src
//
// Created by Yixin Wang on 6/5/2014
//
@ -11,25 +11,24 @@
#ifndef hifi_SentPacketHistory_h
#define hifi_SentPacketHistory_h
#include <stdint.h>
#include <qbytearray.h>
#include <qvector.h>
#include "OctreePacketData.h"
class SentPacketHistory {
public:
SentPacketHistory(int size);
SentPacketHistory(int size = 1000);
void packetSent(OCTREE_PACKET_SEQUENCE sequenceNumber, const QByteArray& packet);
const QByteArray* getPacket(OCTREE_PACKET_SEQUENCE sequenceNumber) const;
void packetSent(uint16_t sequenceNumber, const QByteArray& packet);
const QByteArray* getPacket(uint16_t sequenceNumber) const;
private:
QVector<QByteArray> _sentPackets; // circular buffer
int _newestPacketAt;
int _numExistingPackets;
OCTREE_PACKET_SEQUENCE _newestSequenceNumber;
uint16_t _newestSequenceNumber;
};
#endif

View file

@ -17,7 +17,6 @@
#include <PacketHeaders.h>
#include "OctreeEditPacketSender.h"
EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) :
_nodeUUID(nodeUUID),
_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
// 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();
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
@ -97,12 +96,19 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c
if (node->getType() == getMyNodeType() &&
((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
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...
bool wantDebugging = false;
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)));
quint64 createdAt = (*((quint64*)(buffer + numBytesPacketHeader + sizeof(sequence))));
quint64 queuedAt = usecTimestampNow();
@ -287,18 +293,20 @@ void OctreeEditPacketSender::releaseQueuedMessages() {
if (!serversExist()) {
_releaseQueuedMessagesPending = true;
} else {
for (std::map<QUuid, EditPacketBuffer>::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) {
releaseQueuedPacket(i->second);
for (QHash<QUuid, EditPacketBuffer>::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) {
releaseQueuedPacket(i.value());
}
}
}
void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) {
_releaseQueuedPacketMutex.lock();
if (packetBuffer._currentSize > 0 && packetBuffer._currentType != PacketTypeUnknown) {
queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize);
packetBuffer._currentSize = 0;
packetBuffer._currentType = PacketTypeUnknown;
}
packetBuffer._currentSize = 0;
packetBuffer._currentType = PacketTypeUnknown;
_releaseQueuedPacketMutex.unlock();
}
void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type) {
@ -329,3 +337,41 @@ bool OctreeEditPacketSender::process() {
// base class does most of the work.
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);
}

View file

@ -12,9 +12,11 @@
#ifndef hifi_OctreeEditPacketSender_h
#define hifi_OctreeEditPacketSender_h
#include <qqueue.h>
#include <PacketSender.h>
#include <PacketHeaders.h>
#include "JurisdictionMap.h"
#include "SentPacketHistory.h"
/// Used for construction of edit packets
class EditPacketBuffer {
@ -89,10 +91,16 @@ public:
// you must override these...
virtual char getMyNodeType() const = 0;
virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { };
public slots:
void nodeKilled(SharedNodePointer node);
public:
void processNackPacket(const QByteArray& packet);
protected:
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 queuePacketToNodes(unsigned char* buffer, ssize_t length);
void initializePacket(EditPacketBuffer& packetBuffer, PacketType type);
@ -101,7 +109,7 @@ protected:
void processPreServerExistsPackets();
// 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
int _maxPendingMessages;
@ -114,5 +122,10 @@ protected:
unsigned short int _sequenceNumber;
int _maxPacketSize;
QMutex _releaseQueuedPacketMutex;
// TODO: add locks for this and _pendingEditPackets
QHash<QUuid, SentPacketHistory> _sentPacketHistories;
};
#endif // hifi_OctreeEditPacketSender_h

View file

@ -62,7 +62,7 @@ public:
static bool renderOperation(OctreeElement* element, void* extraData);
/// clears the tree
void clear();
virtual void clear();
protected:
Octree* _tree;
bool _managedTree;

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <limits>
#include <QString>
#include <QStringList>
@ -842,7 +843,7 @@ const char* OctreeSceneStats::getItemValue(Item item) {
}
void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
bool wasStatsPacket, int nodeClockSkewUsec) {
bool wasStatsPacket, int nodeClockSkewUsec) {
const bool wantExtraDebugging = false;
int numBytesPacketHeader = numBytesForPacketHeader(packet);
@ -852,10 +853,10 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
//bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_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
}
const int UINT16_RANGE = std::numeric_limits<uint16_t>::max() + 1;
// determine our expected sequence number... handle rollover appropriately
OCTREE_PACKET_SEQUENCE expected = _incomingPacket > 0 ? _incomingLastSequence + 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());
}
OCTREE_PACKET_SEQUENCE expected = _incomingPacket > 0 ? _incomingLastSequence + (quint16)1 : sequence;
const int USECS_PER_MSEC = 1000;
float flightTimeMsecs = flightTime / USECS_PER_MSEC;
_incomingFlightTimeAverage.updateAverage(flightTimeMsecs);
// track out of order and possibly lost packets...
if (sequence == _incomingLastSequence) {
if (wantExtraDebugging) {
@ -911,15 +898,46 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
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
// 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) {
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++;
} else {
}
else {
_incomingLate++;
}
@ -931,57 +949,78 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
_sequenceNumbersToNack.remove(sequence);
_incomingLikelyLost--;
_incomingRecovered++;
} else {
}
else {
// 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
// duplicate
if (sequence >= std::max(0, (expected - MAX_MISSING_SEQUENCE_OLD_AGE))) {
if (sequenceInt >= expectedInt - MAX_MISSING_SEQUENCE_OLD_AGE) {
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++;
}
}
}
if (sequence > expected) {
// don't update _incomingLastSequence in this case.
// 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) {
qDebug() << "this packet is earlier than expected...";
}
_incomingEarly++;
// 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) {
qDebug() << ">>>>>>>> missing gap=" << 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;
_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
if (_missingSequenceNumbers.size() > MAX_MISSING_SEQUENCE) {
if (wantExtraDebugging) {
qDebug() << "too many _missingSequenceNumbers:" << _missingSequenceNumbers.size();
}
int oldAgeCutoff = (int)_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE;
foreach(uint16_t missingItem, _missingSequenceNumbers) {
if (wantExtraDebugging) {
qDebug() << "checking item:" << missingItem << "is it in need of pruning?";
qDebug() << "(_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE):"
<< (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE);
qDebug() << "(_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) {
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 {

View file

@ -57,7 +57,7 @@ protected:
bool isStillRunning() const { return !_stopThread; }
private:
protected:
QMutex _mutex;
bool _stopThread;