From e953b36f584cf0b107cb9bf3c42fa8007cc0036d Mon Sep 17 00:00:00 2001
From: Stephen Birarda <commit@birarda.com>
Date: Tue, 7 Jul 2015 12:56:20 -0700
Subject: [PATCH] handle stats + data packet combination in OctreeSendThread

---
 .../src/octree/OctreeSendThread.cpp           | 129 ++++++------
 libraries/octree/src/OctreeSceneStats.cpp     | 193 +++++++-----------
 libraries/octree/src/OctreeSceneStats.h       |   8 +-
 3 files changed, 146 insertions(+), 184 deletions(-)

diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index fdd3670a1b..d9ef20553f 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -144,111 +144,84 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
     // If we've got a stats message ready to send, then see if we can piggyback them together
     if (nodeData->stats.isReadyToSend() && !nodeData->isShuttingDown()) {
         // Send the stats message to the client
-        unsigned char* statsMessage = nodeData->stats.getStatsMessage();
-        int statsMessageLength = nodeData->stats.getStatsMessageLength();
-        int piggyBackSize = nodeData->getPacketLength() + statsMessageLength;
+        NLPacket& statsPacket = nodeData->stats.getStatsMessage();
 
         // If the size of the stats message and the octree message will fit in a packet, then piggyback them
-        if (piggyBackSize < MAX_PACKET_SIZE) {
+        if (nodeData->getPacket()->getSizeUsed() < statsPacket->bytesAvailable()) {
 
             // copy octree message to back of stats message
-            memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
-            statsMessageLength += nodeData->getPacketLength();
+            statsPacket->write(nodeData->getPacket()->getData(), nodeData->getPacket()->getSizeWithHeader());
 
             // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
             // there was nothing else to send.
             int thisWastedBytes = 0;
             _totalWastedBytes += thisWastedBytes;
-            _totalBytes += nodeData->getPacketLength();
+            _totalBytes += statsPacket->getSizeWithHeader();
             _totalPackets++;
+
             if (debug) {
-                const unsigned char* messageData = nodeData->getPacket();
-                int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
-                const unsigned char* dataAt = messageData + numBytesPacketHeader;
-                dataAt += sizeof(OCTREE_PACKET_FLAGS);
-                OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
-                dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
-                OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
-                dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
+                NLPacket& sentPacket = nodeData->getPacket();
+
+                sentPacket->seek(sizeof(OCTREE_PACKET_FLAGS));
+
+                OCTREE_PACKET_SEQUENCE sequence;
+                sentPacket->read(&sequence, sizeof(sequence));
+
+                OCTREE_PACKET_SENT_TIME timestamp;
+                sentPacket->read(&timestamp, sizeof(timestamp));
 
                 qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
                         " timestamp: " << timestamp <<
-                        " statsMessageLength: " << statsMessageLength <<
-                        " original size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
+                        " statsMessageLength: " << statsPacket->getSizeWithHeader() <<
+                        " original size: " << nodeData->getPacket()->getSizeWithHeader() << " [" << _totalBytes <<
                         "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
             }
 
             // actually send it
             OctreeServer::didCallWriteDatagram(this);
-            DependencyManager::get<NodeList>()->writeDatagram((char*) statsMessage, statsMessageLength, _node);
+            DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, _node);
             packetSent = true;
         } else {
             // not enough room in the packet, send two packets
             OctreeServer::didCallWriteDatagram(this);
-            DependencyManager::get<NodeList>()->writeDatagram((char*) statsMessage, statsMessageLength, _node);
+            DependencyManager::get<NodeList>()-sendUnreliablePacket(statsPacket, _node);
 
             // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
             // there was nothing else to send.
             int thisWastedBytes = 0;
             _totalWastedBytes += thisWastedBytes;
-            _totalBytes += statsMessageLength;
+            _totalBytes += statsPacket->getSizeWithHeader();
             _totalPackets++;
+
             if (debug) {
-                const unsigned char* messageData = nodeData->getPacket();
-                int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
-                const unsigned char* dataAt = messageData + numBytesPacketHeader;
-                dataAt += sizeof(OCTREE_PACKET_FLAGS);
-                OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
-                dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
-                OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
-                dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
+                NLPacket& sentPacket = nodeData->getPacket();
+
+                sentPacket->seek(sizeof(OCTREE_PACKET_FLAGS));
+
+                OCTREE_PACKET_SEQUENCE sequence;
+                sentPacket->read(&sequence, sizeof(sequence));
+
+                OCTREE_PACKET_SENT_TIME timestamp;
+                sentPacket->read(&timestamp, sizeof(timestamp));
 
                 qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
                         " timestamp: " << timestamp <<
-                        " size: " << statsMessageLength << " [" << _totalBytes <<
+                        " size: " << statsPacket->getSizeWithHeader() << " [" << _totalBytes <<
                         "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
             }
 
-            trueBytesSent += statsMessageLength;
+            trueBytesSent += statsPacket->getSizeWithHeader();
             truePacketsSent++;
             packetsSent++;
 
-            OctreeServer::didCallWriteDatagram(this);
-            DependencyManager::get<NodeList>()->writeDatagram((char*)nodeData->getPacket(), nodeData->getPacketLength(), _node);
-            packetSent = true;
-
-            thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
-            _totalWastedBytes += thisWastedBytes;
-            _totalBytes += nodeData->getPacketLength();
-            _totalPackets++;
-            if (debug) {
-                const unsigned char* messageData = nodeData->getPacket();
-                int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
-                const unsigned char* dataAt = messageData + numBytesPacketHeader;
-                dataAt += sizeof(OCTREE_PACKET_FLAGS);
-                OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
-                dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
-                OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
-                dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
-
-                qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
-                        " timestamp: " << timestamp <<
-                        " size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
-                        "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
-            }
-        }
-        nodeData->stats.markAsSent();
-    } else {
-        // If there's actually a packet waiting, then send it.
-        if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
-            // just send the octree packet
             OctreeServer::didCallWriteDatagram(this);
             DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), _node);
             packetSent = true;
 
-            int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
+            int packetSizeWithHeader = nodeData->getPacket()->getSizeWithHeader();
+            thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
             _totalWastedBytes += thisWastedBytes;
-            _totalBytes += nodeData->getPacketLength();
+            _totalBytes += nodeData->getPacket()->getSizeWithHeader();
             _totalPackets++;
 
             if (debug) {
@@ -264,7 +237,39 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
 
                 qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
                         " timestamp: " << timestamp <<
-                        " size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
+                        " size: " << nodeData->getPacket()->getSizeWithHeader() << " [" << _totalBytes <<
+                        "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
+            }
+        }
+        nodeData->stats.markAsSent();
+    } else {
+        // If there's actually a packet waiting, then send it.
+        if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
+            // just send the octree packet
+            OctreeServer::didCallWriteDatagram(this);
+            DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), _node);
+            packetSent = true;
+
+            int packetSizeWithHeader = nodeData->getPacket()->getSizeWithHeader();
+            int thisWastedBytes = MAX_PACKET_SIZE -;
+            _totalWastedBytes += thisWastedBytes;
+            _totalBytes += packetSizeWithHeader;
+            _totalPackets++;
+
+            if (debug) {
+                NLPacket& sentPacket = nodeData->getPacket();
+
+                sentPacket->seek(sizeof(OCTREE_PACKET_FLAGS));
+
+                OCTREE_PACKET_SEQUENCE sequence;
+                sentPacket->read(&sequence, sizeof(sequence));
+
+                OCTREE_PACKET_SENT_TIME timestamp;
+                sentPacket->read(&timestamp, sizeof(timestamp));
+
+                qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
+                        " timestamp: " << timestamp <<
+                        " size: " << packetSizeWithHeader << " [" << _totalBytes <<
                         "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
             }
         }
diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp
index 0d24012c24..9141b0a989 100644
--- a/libraries/octree/src/OctreeSceneStats.cpp
+++ b/libraries/octree/src/OctreeSceneStats.cpp
@@ -24,14 +24,13 @@
 
 
 const int samples = 100;
-OctreeSceneStats::OctreeSceneStats() : 
+OctreeSceneStats::OctreeSceneStats() :
     _isReadyToSend(false),
     _isStarted(false),
     _lastFullElapsed(0),
     _lastFullTotalEncodeTime(0),
     _lastFullTotalPackets(0),
     _lastFullTotalBytes(0),
-
     _elapsedAverage(samples),
     _bitsPerOctreeAverage(samples),
     _incomingPacket(0),
@@ -121,8 +120,8 @@ void OctreeSceneStats::copyFromOther(const OctreeSceneStats& other) {
         }
     }
     _jurisdictionEndNodes.clear();
-    
-    // Now copy the values from the other    
+
+    // Now copy the values from the other
     if (other._jurisdictionRoot) {
         int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(other._jurisdictionRoot));
         _jurisdictionRoot = new unsigned char[bytes];
@@ -154,14 +153,14 @@ void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, OctreeEleme
     reset(); // resets packet and octree stats
     _isStarted = true;
     _start = usecTimestampNow();
-    
+
     _totalElements = OctreeElement::getNodeCount();
     _totalInternal = OctreeElement::getInternalNodeCount();
     _totalLeaves   = OctreeElement::getLeafNodeCount();
-    
+
     _isFullScene = isFullScene;
     _isMoving = isMoving;
-    
+
     if (_jurisdictionRoot) {
         delete[] _jurisdictionRoot;
         _jurisdictionRoot = NULL;
@@ -201,13 +200,13 @@ void OctreeSceneStats::sceneCompleted() {
         _end = usecTimestampNow();
         _elapsed = _end - _start;
         _elapsedAverage.updateAverage((float)_elapsed);
-        
+
         if (_isFullScene) {
             _lastFullElapsed = _elapsed;
             _lastFullTotalEncodeTime = _totalEncodeTime;
         }
 
-        _statsMessageLength = packIntoMessage(_statsMessage, sizeof(_statsMessage));
+        packIntoPacket();
         _isReadyToSend = true;
         _isStarted = false;
     }
@@ -382,105 +381,65 @@ void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesCo
     _treesRemoved++;
 }
 
-int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
-    unsigned char* bufferStart = destinationBuffer;
-    
-    int headerLength = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(destinationBuffer), 
-                                                                                PacketTypeOctreeStats);
-    destinationBuffer += headerLength;
-    
-    memcpy(destinationBuffer, &_start, sizeof(_start));
-    destinationBuffer += sizeof(_start);
-    memcpy(destinationBuffer, &_end, sizeof(_end));
-    destinationBuffer += sizeof(_end);
-    memcpy(destinationBuffer, &_elapsed, sizeof(_elapsed));
-    destinationBuffer += sizeof(_elapsed);
-    memcpy(destinationBuffer, &_totalEncodeTime, sizeof(_totalEncodeTime));
-    destinationBuffer += sizeof(_totalEncodeTime);
-    memcpy(destinationBuffer, &_isFullScene, sizeof(_isFullScene));
-    destinationBuffer += sizeof(_isFullScene);
-    memcpy(destinationBuffer, &_isMoving, sizeof(_isMoving));
-    destinationBuffer += sizeof(_isMoving);
-    memcpy(destinationBuffer, &_packets, sizeof(_packets));
-    destinationBuffer += sizeof(_packets);
-    memcpy(destinationBuffer, &_bytes, sizeof(_bytes));
-    destinationBuffer += sizeof(_bytes);
+int OctreeSceneStats::packIntoPacket() {
+    _statsPacket->reset();
 
-    memcpy(destinationBuffer, &_totalInternal, sizeof(_totalInternal));
-    destinationBuffer += sizeof(_totalInternal);
-    memcpy(destinationBuffer, &_totalLeaves, sizeof(_totalLeaves));
-    destinationBuffer += sizeof(_totalLeaves);
-    memcpy(destinationBuffer, &_internal, sizeof(_internal));
-    destinationBuffer += sizeof(_internal);
-    memcpy(destinationBuffer, &_leaves, sizeof(_leaves));
-    destinationBuffer += sizeof(_leaves);
-    memcpy(destinationBuffer, &_internalSkippedDistance, sizeof(_internalSkippedDistance));
-    destinationBuffer += sizeof(_internalSkippedDistance);
-    memcpy(destinationBuffer, &_leavesSkippedDistance, sizeof(_leavesSkippedDistance));
-    destinationBuffer += sizeof(_leavesSkippedDistance);
-    memcpy(destinationBuffer, &_internalSkippedOutOfView, sizeof(_internalSkippedOutOfView));
-    destinationBuffer += sizeof(_internalSkippedOutOfView);
-    memcpy(destinationBuffer, &_leavesSkippedOutOfView, sizeof(_leavesSkippedOutOfView));
-    destinationBuffer += sizeof(_leavesSkippedOutOfView);
-    memcpy(destinationBuffer, &_internalSkippedWasInView, sizeof(_internalSkippedWasInView));
-    destinationBuffer += sizeof(_internalSkippedWasInView);
-    memcpy(destinationBuffer, &_leavesSkippedWasInView, sizeof(_leavesSkippedWasInView));
-    destinationBuffer += sizeof(_leavesSkippedWasInView);
-    memcpy(destinationBuffer, &_internalSkippedNoChange, sizeof(_internalSkippedNoChange));
-    destinationBuffer += sizeof(_internalSkippedNoChange);
-    memcpy(destinationBuffer, &_leavesSkippedNoChange, sizeof(_leavesSkippedNoChange));
-    destinationBuffer += sizeof(_leavesSkippedNoChange);
-    memcpy(destinationBuffer, &_internalSkippedOccluded, sizeof(_internalSkippedOccluded));
-    destinationBuffer += sizeof(_internalSkippedOccluded);
-    memcpy(destinationBuffer, &_leavesSkippedOccluded, sizeof(_leavesSkippedOccluded));
-    destinationBuffer += sizeof(_leavesSkippedOccluded);
-    memcpy(destinationBuffer, &_internalColorSent, sizeof(_internalColorSent));
-    destinationBuffer += sizeof(_internalColorSent);
-    memcpy(destinationBuffer, &_leavesColorSent, sizeof(_leavesColorSent));
-    destinationBuffer += sizeof(_leavesColorSent);
-    memcpy(destinationBuffer, &_internalDidntFit, sizeof(_internalDidntFit));
-    destinationBuffer += sizeof(_internalDidntFit);
-    memcpy(destinationBuffer, &_leavesDidntFit, sizeof(_leavesDidntFit));
-    destinationBuffer += sizeof(_leavesDidntFit);
-    memcpy(destinationBuffer, &_colorBitsWritten, sizeof(_colorBitsWritten));
-    destinationBuffer += sizeof(_colorBitsWritten);
-    memcpy(destinationBuffer, &_existsBitsWritten, sizeof(_existsBitsWritten));
-    destinationBuffer += sizeof(_existsBitsWritten);
-    memcpy(destinationBuffer, &_existsInPacketBitsWritten, sizeof(_existsInPacketBitsWritten));
-    destinationBuffer += sizeof(_existsInPacketBitsWritten);
-    memcpy(destinationBuffer, &_treesRemoved, sizeof(_treesRemoved));
-    destinationBuffer += sizeof(_treesRemoved);
+    _statsPacket->write(&_start, sizeof(_start));
+    _statsPacket->write(&_end, sizeof(_end));
+    _statsPacket->write(&_elapsed, sizeof(_elapsed));
+    _statsPacket->write(&_totalEncodeTime, sizeof(_totalEncodeTime));
+    _statsPacket->write(&_isFullScene, sizeof(_isFullScene));
+    _statsPacket->write(&_isMoving, sizeof(_isMoving));
+    _statsPacket->write(&_packets, sizeof(_packets));
+    _statsPacket->write(&_bytes, sizeof(_bytes));
+
+    _statsPacket->write(&_totalInternal, sizeof(_totalInternal));
+    _statsPacket->write(&_totalLeaves, sizeof(_totalLeaves));
+    _statsPacket->write(&_internal, sizeof(_internal));
+    _statsPacket->write(&_leaves, sizeof(_leaves));
+    _statsPacket->write(&_internalSkippedDistance, sizeof(_internalSkippedDistance));
+    _statsPacket->write(&_leavesSkippedDistance, sizeof(_leavesSkippedDistance));
+    _statsPacket->write(&_internalSkippedOutOfView, sizeof(_internalSkippedOutOfView));
+    _statsPacket->write(&_leavesSkippedOutOfView, sizeof(_leavesSkippedOutOfView));
+    _statsPacket->write(&_internalSkippedWasInView, sizeof(_internalSkippedWasInView));
+    _statsPacket->write(&_leavesSkippedWasInView, sizeof(_leavesSkippedWasInView));
+    _statsPacket->write(&_internalSkippedNoChange, sizeof(_internalSkippedNoChange));
+    _statsPacket->write(&_leavesSkippedNoChange, sizeof(_leavesSkippedNoChange));
+    _statsPacket->write(&_internalSkippedOccluded, sizeof(_internalSkippedOccluded));
+    _statsPacket->write(&_leavesSkippedOccluded, sizeof(_leavesSkippedOccluded));
+    _statsPacket->write(&_internalColorSent, sizeof(_internalColorSent));
+    _statsPacket->write(&_leavesColorSent, sizeof(_leavesColorSent));
+    _statsPacket->write(&_internalDidntFit, sizeof(_internalDidntFit));
+    _statsPacket->write(&_leavesDidntFit, sizeof(_leavesDidntFit));
+    _statsPacket->write(&_colorBitsWritten, sizeof(_colorBitsWritten));
+    _statsPacket->write(&_existsBitsWritten, sizeof(_existsBitsWritten));
+    _statsPacket->write(&_existsInPacketBitsWritten, sizeof(_existsInPacketBitsWritten));
+    _statsPacket->write(&_treesRemoved, sizeof(_treesRemoved));
 
     // add the root jurisdiction
     if (_jurisdictionRoot) {
-        // copy the 
+        // copy the
         int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_jurisdictionRoot));
-        memcpy(destinationBuffer, &bytes, sizeof(bytes));
-        destinationBuffer += sizeof(bytes);
-        memcpy(destinationBuffer, _jurisdictionRoot, bytes);
-        destinationBuffer += bytes;
-        
-        // if and only if there's a root jurisdiction, also include the end elements
-        int endNodeCount = _jurisdictionEndNodes.size(); 
+        _statsPacket->write(&bytes, sizeof(bytes));
+        _statsPacket->write(_jurisdictionRoot, bytes);
 
-        memcpy(destinationBuffer, &endNodeCount, sizeof(endNodeCount));
-        destinationBuffer += sizeof(endNodeCount);
+        // if and only if there's a root jurisdiction, also include the end elements
+        int endNodeCount = _jurisdictionEndNodes.size();
+
+        _statsPacket->write(&endNodeCount, sizeof(endNodeCount));
 
         for (int i=0; i < endNodeCount; i++) {
             unsigned char* endNodeCode = _jurisdictionEndNodes[i];
             int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode));
-            memcpy(destinationBuffer, &bytes, sizeof(bytes));
-            destinationBuffer += sizeof(bytes);
-            memcpy(destinationBuffer, endNodeCode, bytes);
-            destinationBuffer += bytes;
+            _statsPacket->write(&bytes, sizeof(bytes));
+            _statsPacket->write(endNodeCode, bytes);
         }
     } else {
         int bytes = 0;
-        memcpy(destinationBuffer, &bytes, sizeof(bytes));
-        destinationBuffer += sizeof(bytes);
+        _statsPacket->write(&bytes, sizeof(bytes));
     }
-    
-    return destinationBuffer - bufferStart; // includes header!
+
+    return _statsPacket->getSizeUsed();
 }
 
 int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes) {
@@ -489,7 +448,7 @@ int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int a
     // increment to push past the packet header
     int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(sourceBuffer));
     sourceBuffer += numBytesPacketHeader;
-    
+
     memcpy(&_start, sourceBuffer, sizeof(_start));
     sourceBuffer += sizeof(_start);
     memcpy(&_end, sourceBuffer, sizeof(_end));
@@ -526,13 +485,13 @@ int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int a
     memcpy(&_leaves, sourceBuffer, sizeof(_leaves));
     sourceBuffer += sizeof(_leaves);
     _traversed = _internal + _leaves;
-  
+
     memcpy(&_internalSkippedDistance, sourceBuffer, sizeof(_internalSkippedDistance));
     sourceBuffer += sizeof(_internalSkippedDistance);
     memcpy(&_leavesSkippedDistance, sourceBuffer, sizeof(_leavesSkippedDistance));
     sourceBuffer += sizeof(_leavesSkippedDistance);
     _skippedDistance = _internalSkippedDistance + _leavesSkippedDistance;
-    
+
     memcpy(&_internalSkippedOutOfView, sourceBuffer, sizeof(_internalSkippedOutOfView));
     sourceBuffer += sizeof(_internalSkippedOutOfView);
     memcpy(&_leavesSkippedOutOfView, sourceBuffer, sizeof(_leavesSkippedOutOfView));
@@ -619,7 +578,7 @@ int OctreeSceneStats::unpackFromMessage(const unsigned char* sourceBuffer, int a
             _jurisdictionEndNodes.push_back(endNodeCode);
         }
     }
-    
+
     // running averages
     _elapsedAverage.updateAverage((float)_elapsed);
     unsigned long total = _existsInPacketBitsWritten + _colorSent;
@@ -732,75 +691,75 @@ const char* OctreeSceneStats::getItemValue(Item item) {
             unsigned long total = _existsInPacketBitsWritten + _colorSent;
             float calculatedBPV = total == 0 ? 0 : (_bytes * 8) / total;
             float averageBPV = _bitsPerOctreeAverage.getAverage();
-            sprintf(_itemValueBuffer, "%lu (%.2f bits/octree Average: %.2f bits/octree) %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu (%.2f bits/octree Average: %.2f bits/octree) %lu internal %lu leaves",
                     total, (double)calculatedBPV, (double)averageBPV,
                     (long unsigned int)_existsInPacketBitsWritten,
                     (long unsigned int)_colorSent);
             break;
         }
         case ITEM_TRAVERSED: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     (long unsigned int)_traversed, (long unsigned int)_internal, (long unsigned int)_leaves);
             break;
         }
         case ITEM_SKIPPED: {
-            unsigned long total    = _skippedDistance + _skippedOutOfView + 
+            unsigned long total    = _skippedDistance + _skippedOutOfView +
                                      _skippedWasInView + _skippedNoChange + _skippedOccluded;
-                                     
-            unsigned long internal = _internalSkippedDistance + _internalSkippedOutOfView + 
+
+            unsigned long internal = _internalSkippedDistance + _internalSkippedOutOfView +
                                      _internalSkippedWasInView + _internalSkippedNoChange + _internalSkippedOccluded;
-                                     
-            unsigned long leaves   = _leavesSkippedDistance + _leavesSkippedOutOfView + 
+
+            unsigned long leaves   = _leavesSkippedDistance + _leavesSkippedOutOfView +
                                      _leavesSkippedWasInView + _leavesSkippedNoChange + _leavesSkippedOccluded;
 
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     total, internal, leaves);
             break;
         }
         case ITEM_SKIPPED_DISTANCE: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     (long unsigned int)_skippedDistance,
                     (long unsigned int)_internalSkippedDistance,
                     (long unsigned int)_leavesSkippedDistance);
             break;
         }
         case ITEM_SKIPPED_OUT_OF_VIEW: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     (long unsigned int)_skippedOutOfView,
                     (long unsigned int)_internalSkippedOutOfView,
                     (long unsigned int)_leavesSkippedOutOfView);
             break;
         }
         case ITEM_SKIPPED_WAS_IN_VIEW: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     (long unsigned int)_skippedWasInView,
                     (long unsigned int)_internalSkippedWasInView,
                     (long unsigned int)_leavesSkippedWasInView);
             break;
         }
         case ITEM_SKIPPED_NO_CHANGE: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     (long unsigned int)_skippedNoChange,
                     (long unsigned int)_internalSkippedNoChange,
                     (long unsigned int)_leavesSkippedNoChange);
             break;
         }
         case ITEM_SKIPPED_OCCLUDED: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     (long unsigned int)_skippedOccluded,
                     (long unsigned int)_internalSkippedOccluded,
                     (long unsigned int)_leavesSkippedOccluded);
             break;
         }
         case ITEM_COLORS: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves",
                     (long unsigned int)_colorSent,
                     (long unsigned int)_internalColorSent,
                     (long unsigned int)_leavesColorSent);
             break;
         }
         case ITEM_DIDNT_FIT: {
-            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves (removed: %lu)", 
+            sprintf(_itemValueBuffer, "%lu total %lu internal %lu leaves (removed: %lu)",
                     (long unsigned int)_didntFit,
                     (long unsigned int)_internalDidntFit,
                     (long unsigned int)_leavesDidntFit,
@@ -808,14 +767,14 @@ const char* OctreeSceneStats::getItemValue(Item item) {
             break;
         }
         case ITEM_BITS: {
-            sprintf(_itemValueBuffer, "colors: %lu, exists: %lu, in packets: %lu", 
+            sprintf(_itemValueBuffer, "colors: %lu, exists: %lu, in packets: %lu",
                     (long unsigned int)_colorBitsWritten,
                     (long unsigned int)_existsBitsWritten,
                     (long unsigned int)_existsInPacketBitsWritten);
             break;
         }
         case ITEM_MODE: {
-            sprintf(_itemValueBuffer, "%s - %s", (_isFullScene ? "Full Scene" : "Partial Scene"), 
+            sprintf(_itemValueBuffer, "%s - %s", (_isFullScene ? "Full Scene" : "Partial Scene"),
                     (_isMoving ? "Moving" : "Stationary"));
             break;
         }
@@ -842,7 +801,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
 
     //bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT);
     //bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
-    
+
     OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
     qint64 flightTime = arrivedAt - sentAt + nodeClockSkewUsec;
 
@@ -865,7 +824,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
                     << "nodeClockSkewUsec:" << nodeClockSkewUsec << "usecs";;
         return; // ignore any packets that are unreasonable
     }
-    
+
     _incomingOctreeSequenceNumberStats.sequenceNumberReceived(sequence);
 
     // track packets here...
diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h
index fcb0c0415d..901a9f1544 100644
--- a/libraries/octree/src/OctreeSceneStats.h
+++ b/libraries/octree/src/OctreeSceneStats.h
@@ -91,7 +91,7 @@ public:
     void childBitsRemoved(bool includesExistsBits, bool includesColors);
 
     /// Pack the details of the statistics into a buffer for sending as a network packet
-    int packIntoMessage(unsigned char* destinationBuffer, int availableBytes);
+    int packIntoPacket();
 
     /// Unpack the details of the statistics from a buffer typically received as a network packet
     int unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes);
@@ -102,8 +102,7 @@ public:
     /// Mark that the scene statistics have been sent
     void markAsSent() { _isReadyToSend = false; }
 
-    unsigned char* getStatsMessage() { return &_statsMessage[0]; }
-    int getStatsMessageLength() const { return _statsMessageLength; }
+    NLPacket& getStatsMessage() { return *_statsPacket; }
 
     /// List of various items tracked by OctreeSceneStats which can be accessed via getItemInfo() and getItemValue()
     enum Item {
@@ -176,9 +175,8 @@ private:
     void copyFromOther(const OctreeSceneStats& other);
 
     bool _isReadyToSend;
-    unsigned char _statsMessage[MAX_PACKET_SIZE];
 
-    qint32 _statsMessageLength;
+    std::unique_ptr<NLPacket> _statsPacket { NLPacket::create(PacketType::OctreeStats); }
 
     // scene timing data in usecs
     bool _isStarted;