diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp deleted file mode 100644 index 4af48f3299..0000000000 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/***** - -// -// VoxelSendThread.cpp -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded voxel packet sender -// - -#include -#include -#include -#include -#include - -extern EnvironmentData environmentData[3]; - - -#include "VoxelSendThread.h" -#include "VoxelServer.h" -#include "VoxelServerConsts.h" - - -uint64_t startSceneSleepTime = 0; -uint64_t endSceneSleepTime = 0; - - -VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) : - _nodeUUID(nodeUUID), - _myServer(myServer), - _packetData() -{ -} - -bool VoxelSendThread::process() { - uint64_t start = usecTimestampNow(); - bool gotLock = false; - - // don't do any send processing until the initial load of the voxels is complete... - if (_myServer->isInitialLoadComplete()) { - Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); - - if (node) { - // make sure the node list doesn't kill our node while we're using it - if (node->trylock()) { - gotLock = true; - VoxelNodeData* nodeData = NULL; - - nodeData = (VoxelNodeData*) node->getLinkedData(); - - int packetsSent = 0; - - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); - } - packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); - } - - node->unlock(); // we're done with this node for now. - } - } - } else { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n"); - } - } - - // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap - if (isStillRunning() && gotLock) { - // dynamically sleep until we need to fire off the next set of voxels - int elapsed = (usecTimestampNow() - start); - int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed; - - if (usecToSleep > 0) { - PerformanceWarning warn(false,"VoxelSendThread... usleep()",false,&_usleepTime,&_usleepCalls); - usleep(usecToSleep); - } else { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - std::cout << "Last send took too much time, not sleeping!\n"; - } - } - } - - return isStillRunning(); // keep running till they terminate us -} - -uint64_t VoxelSendThread::_usleepTime = 0; -uint64_t VoxelSendThread::_usleepCalls = 0; - -uint64_t VoxelSendThread::_totalBytes = 0; -uint64_t VoxelSendThread::_totalWastedBytes = 0; -uint64_t VoxelSendThread::_totalPackets = 0; - -int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) { - bool debug = _myServer->wantsDebugVoxelSending(); - uint64_t now = usecTimestampNow(); - - bool packetSent = false; // did we send a packet? - int packetsSent = 0; - // Here's where we check to see if this packet is a duplicate of the last packet. If it is, we will silently - // obscure the packet and not send it. This allows the callers and upper level logic to not need to know about - // this rate control savings. - if (nodeData->shouldSuppressDuplicatePacket()) { - nodeData->resetVoxelPacket(true); // we still need to reset it though! - return packetsSent; // without sending... - } - - const unsigned char* messageData = nodeData->getPacket(); - int numBytesPacketHeader = numBytesForPacketHeader(messageData); - const unsigned char* dataAt = messageData + numBytesPacketHeader; - dataAt += sizeof(VOXEL_PACKET_FLAGS); - VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); - dataAt += sizeof(VOXEL_PACKET_SEQUENCE); - - - // If we've got a stats message ready to send, then see if we can piggyback them together - if (nodeData->stats.isReadyToSend()) { - // Send the stats message to the client - unsigned char* statsMessage = nodeData->stats.getStatsMessage(); - int statsMessageLength = nodeData->stats.getStatsMessageLength(); - - // If the size of the stats message and the voxel message will fit in a packet, then piggyback them - if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { - - // copy voxel message to back of stats message - memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); - statsMessageLength += nodeData->getPacketLength(); - - // 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(); - _totalPackets++; - if (debug) { - qDebug("Adding stats to packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - sequence, nodeData->getPacketLength(), _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - - // actually send it - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - packetSent = true; - } else { - // not enough room in the packet, send two packets - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - - // 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; - _totalPackets++; - if (debug) { - qDebug("Sending separate stats packet at %llu [%llu]: size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - statsMessageLength, _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - - trueBytesSent += statsMessageLength; - truePacketsSent++; - packetsSent++; - - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - - packetSent = true; - - thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); - _totalWastedBytes += thisWastedBytes; - _totalBytes += nodeData->getPacketLength(); - _totalPackets++; - if (debug) { - qDebug("Sending packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - sequence, nodeData->getPacketLength(), _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - } - nodeData->stats.markAsSent(); - } else { - // If there's actually a packet waiting, then send it. - if (nodeData->isPacketWaiting()) { - // just send the voxel packet - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - packetSent = true; - - int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); - _totalWastedBytes += thisWastedBytes; - _totalBytes += nodeData->getPacketLength(); - _totalPackets++; - if (debug) { - qDebug("Sending packet at %llu [%llu]: sequence:%d size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - sequence, nodeData->getPacketLength(), _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - } - } - // remember to track our stats - if (packetSent) { - nodeData->stats.packetSent(nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - packetsSent++; - nodeData->resetVoxelPacket(); - } - - return packetsSent; -} - -/// Version of voxel distributor that sends the deepest LOD level at once -int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) { - - int truePacketsSent = 0; - int trueBytesSent = 0; - int packetsSentThisInterval = 0; - bool somethingToSend = true; // assume we have something - - // FOR NOW... node tells us if it wants to receive only view frustum deltas - bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); - - // If our packet already has content in it, then we must use the color choice of the waiting packet. - // If we're starting a fresh packet, then... - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use - // the clients requested color state. - bool wantColor = nodeData->getWantColor(); - bool wantCompression = nodeData->getWantCompression(); - - // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color - // then let's just send that waiting packet. - if (!nodeData->getCurrentPacketFormatMatches()) { - if (nodeData->isPacketWaiting()) { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", - debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); - } - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - } else { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", - debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); - } - nodeData->resetVoxelPacket(); - } - int targetSize = MAX_VOXEL_PACKET_DATA_SIZE; - if (wantCompression) { - targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - } - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, - debug::valueOf(wantCompression), targetSize); - } - - _packetData.changeSettings(wantCompression, targetSize); - } - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()), - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); - } - - const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), - debug::valueOf(nodeData->getViewSent()) - ); - } - - // If the current view frustum has changed OR we have nothing to send, then search against - // the current view frustum for things to send. - if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { - uint64_t now = usecTimestampNow(); - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); - if (nodeData->getLastTimeBagEmpty() > 0) { - float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; - if (viewFrustumChanged) { - printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); - } else { - printf("elapsed time to send scene = %f seconds", elapsedSceneSend); - } - printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), - debug::valueOf(wantColor)); - } - } - - // if our view has changed, we need to reset these things... - if (viewFrustumChanged) { - if (_myServer->wantDumpVoxelsOnMove() || nodeData->moveShouldDump() || nodeData->hasLodChanged()) { - nodeData->dumpOutOfView(); - } - nodeData->map.erase(); - } - - if (!viewFrustumChanged && !nodeData->getWantDelta()) { - // only set our last sent time if we weren't resetting due to frustum change - uint64_t now = usecTimestampNow(); - nodeData->setLastTimeBagEmpty(now); - } - - // track completed scenes and send out the stats packet accordingly - nodeData->stats.sceneCompleted(); - ::endSceneSleepTime = _usleepTime; - unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime; - - unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); - unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - - if (_myServer->wantsDebugVoxelSending()) { - qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n", - usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes); - } - - if (_myServer->wantDisplayVoxelStats()) { - nodeData->stats.printDebugDetails(); - } - - // start tracking our stats - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) - && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - - // If we're starting a full scene, then definitely we want to empty the nodeBag - if (isFullScene) { - nodeData->nodeBag.deleteAll(); - } - - if (_myServer->wantsDebugVoxelSending()) { - qDebug("Scene started at %llu Packets:%llu Bytes:%llu Wasted:%llu\n", - usecTimestampNow(),_totalPackets,_totalBytes,_totalWastedBytes); - } - - ::startSceneSleepTime = _usleepTime; - nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getServerTree().getRoot(), _myServer->getJurisdiction()); - - // This is the start of "resending" the scene. - bool dontRestartSceneOnMove = false; // this is experimental - if (dontRestartSceneOnMove) { - if (nodeData->nodeBag.isEmpty()) { - nodeData->nodeBag.insert(_myServer->getServerTree().getRoot()); // only in case of empty - } - } else { - nodeData->nodeBag.insert(_myServer->getServerTree().getRoot()); // original behavior, reset on move or empty - } - } - - // If we have something in our nodeBag, then turn them into packets and send them out... - if (!nodeData->nodeBag.isEmpty()) { - int bytesWritten = 0; - uint64_t start = usecTimestampNow(); - uint64_t startCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000; - uint64_t startCompressCalls = VoxelPacketData::getCompressContentCalls(); - - bool shouldSendEnvironments = _myServer->wantSendEnvironments() && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); - - int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); - int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } - - int extraPackingAttempts = 0; - while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } - - bool lastNodeDidntFit = false; // assume each node fits - if (!nodeData->nodeBag.isEmpty()) { - OctreeElement* subTree = nodeData->nodeBag.extract(); - bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); - CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - - float voxelSizeScale = nodeData->getOctreeSizeScale(); - int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); - - int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() - ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - - - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, - WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, - nodeData->getLastTimeBagEmpty(), - isFullScene, &nodeData->stats, _myServer->getJurisdiction()); - - - _myServer->getServerTree().lockForRead(); - nodeData->stats.encodeStarted(); - bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); - - // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. - if (_packetData.getTargetSize() == MAX_VOXEL_PACKET_DATA_SIZE) { - if (_packetData.hasContent() && bytesWritten == 0 && - params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; - } - } else { - // in compressed mode and we are trying to pack more... and we don't care if the _packetData has - // content or not... because in this case even if we were unable to pack any data, we want to drop - // below to our sendNow logic, but we do want to track that we attempted to pack extra - extraPackingAttempts++; - if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; - } - } - - nodeData->stats.encodeStopped(); - _myServer->getServerTree().unlock(); - } else { - // If the bag was empty then we didn't even attempt to encode, and so we know the bytesWritten were 0 - bytesWritten = 0; - somethingToSend = false; // this will cause us to drop out of the loop... - } - - // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a - // little bit more in this packet. To do this we - - // We only consider sending anything if there is something in the _packetData to send... But - // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases - // mean we should send the previous packet contents and reset it. - if (lastNodeDidntFit) { - if (_packetData.hasContent()) { - // if for some reason the finalized size is greater than our available size, then probably the "compressed" - // form actually inflated beyond our padding, and in this case we will send the current packet, then - // write to out new packet... - int writtenSize = _packetData.getFinalizedSize() - + (nodeData->getCurrentPacketIsCompressed() ? sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) : 0); - - - if (writtenSize > nodeData->getAvailable()) { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n", - writtenSize, nodeData->getAvailable()); - } - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - } - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", - nodeData->getAvailable(), _packetData.getFinalizedSize(), - _packetData.getUncompressedSize(), _packetData.getTargetSize()); - } - nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); - extraPackingAttempts = 0; - } - - // If we're not running compressed, the we know we can just send now. Or if we're running compressed, but - // the packet doesn't have enough space to bother attempting to pack more... - bool sendNow = true; - - if (nodeData->getCurrentPacketIsCompressed() && - nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && - extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) { - sendNow = false; // try to pack more - } - - int targetSize = MAX_VOXEL_PACKET_DATA_SIZE; - if (sendNow) { - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - if (wantCompression) { - targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - } - } else { - // If we're in compressed mode, then we want to see if we have room for more in this wire packet. - // but we've finalized the _packetData, so we want to start a new section, we will do that by - // resetting the packet settings with the max uncompressed size of our current available space - // in the wire packet. We also include room for our section header, and a little bit of padding - // to account for the fact that whenc compressing small amounts of data, we sometimes end up with - // a larger compressed size then uncompressed size - targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; - } - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, - debug::valueOf(nodeData->getWantCompression()), targetSize); - } - _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset - } - } - - // send the environment packet - if (shouldSendEnvironments) { - int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); - int envPacketLength = numBytesPacketHeader; - int environmentsToSend = _myServer->getSendMinimalEnvironment() ? 1 : _myServer->getEnvironmentDataCount(); - - for (int i = 0; i < environmentsToSend; i++) { - envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength); - } - - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - trueBytesSent += envPacketLength; - truePacketsSent++; - packetsSentThisInterval++; - } - - uint64_t end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - - uint64_t endCompressCalls = VoxelPacketData::getCompressContentCalls(); - int elapsedCompressCalls = endCompressCalls - startCompressCalls; - - uint64_t endCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000; - int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; - - - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } else { - printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - } else if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all - // the voxels from the current view frustum - if (nodeData->nodeBag.isEmpty()) { - nodeData->updateLastKnownViewFrustum(); - nodeData->setViewSent(true); - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - nodeData->map.printStats(); - } - nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes - } - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } - - } // end if bag wasn't empty, and so we sent stuff... - - return truePacketsSent; -} - -****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 6e68b05170..6408d356a5 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -43,748 +43,40 @@ const char* VoxelServer::getMyDefaultPersistFilename() { return LOCAL_VOXELS_PERSIST_FILE; } -/***** - - -void attachVoxelNodeDataToNode(Node* newNode) { - if (newNode->getLinkedData() == NULL) { - VoxelNodeData* voxelNodeData = new VoxelNodeData(newNode); - newNode->setLinkedData(voxelNodeData); - } +bool VoxelServer::hasSpecialPacketToSend() { + bool shouldSendEnvironments = _sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, OCTREE_SEND_INTERVAL_USECS); + return shouldSendEnvironments; } -VoxelServer* VoxelServer::_theInstance = NULL; - -VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes), - _serverTree(true) -{ - _argc = 0; - _argv = NULL; - - _packetsPerClientPerInterval = 10; - _wantVoxelPersist = true; - _wantLocalDomain = false; - _debugVoxelSending = false; - _shouldShowAnimationDebug = false; - _displayVoxelStats = false; - _debugVoxelReceiving = false; - _sendEnvironments = true; - _sendMinimalEnvironment = false; - _dumpVoxelsOnMove = false; - _verboseDebug = false; - _jurisdiction = NULL; - _jurisdictionSender = NULL; - _voxelServerPacketProcessor = NULL; - _voxelPersistThread = NULL; - _parsedArgV = NULL; +int VoxelServer::sendSpecialPacket(Node* node) { + int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); + int envPacketLength = numBytesPacketHeader; + int environmentsToSend = getSendMinimalEnvironment() ? 1 : getEnvironmentDataCount(); - _started = time(0); - _startedUSecs = usecTimestampNow(); - - _theInstance = this; -} - -VoxelServer::~VoxelServer() { - if (_parsedArgV) { - for (int i = 0; i < _argc; i++) { - delete[] _parsedArgV[i]; - } - delete[] _parsedArgV; - } -} - -void VoxelServer::initMongoose(int port) { - // setup the mongoose web server - struct mg_callbacks callbacks = {}; - - QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath()); - QString listenPort = QString("%1").arg(port); - - - // list of options. Last element must be NULL. - const char* options[] = { - "listening_ports", listenPort.toLocal8Bit().constData(), - "document_root", documentRoot.toLocal8Bit().constData(), - NULL }; - - callbacks.begin_request = civetwebRequestHandler; - - // Start the web server. - mg_start(&callbacks, NULL, options); -} - -int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) { - const struct mg_request_info* ri = mg_get_request_info(connection); - - VoxelServer* theServer = GetInstance(); - -#ifdef FORCE_CRASH - if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) { - qDebug() << "About to force a crash!\n"; - int foo; - int* forceCrash = &foo; - mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n"); - mg_printf(connection, "%s", "forcing a crash....\r\n"); - delete[] forceCrash; - mg_printf(connection, "%s", "did it crash....\r\n"); - return 1; - } -#endif - - bool showStats = false; - if (strcmp(ri->uri, "/") == 0 && strcmp(ri->request_method, "GET") == 0) { - showStats = true; - } - - if (strcmp(ri->uri, "/resetStats") == 0 && strcmp(ri->request_method, "GET") == 0) { - theServer->_voxelServerPacketProcessor->resetStats(); - showStats = true; + for (int i = 0; i < environmentsToSend; i++) { + envPacketLength += getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength); } - if (showStats) { - uint64_t checkSum; - // return a 200 - mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n"); - mg_printf(connection, "%s", "Content-Type: text/html\r\n\r\n"); - mg_printf(connection, "%s", "\r\n"); - mg_printf(connection, "%s", "
\r\n");
-        mg_printf(connection, "%s", "Your Voxel Server is running... [RELOAD]\r\n");
-
-        tm* localtm = localtime(&theServer->_started);
-        const int MAX_TIME_LENGTH = 128;
-        char buffer[MAX_TIME_LENGTH];
-        strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
-        mg_printf(connection, "Running since: %s", buffer);
-
-        // Convert now to tm struct for UTC
-        tm* gmtm = gmtime(&theServer->_started);
-        if (gmtm != NULL) {
-            strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
-            mg_printf(connection, " [%s UTM] ", buffer);
-        }
-        mg_printf(connection, "%s", "\r\n");
-
-        uint64_t now  = usecTimestampNow();
-        const int USECS_PER_MSEC = 1000;
-        uint64_t msecsElapsed = (now - theServer->_startedUSecs) / USECS_PER_MSEC;
-        const int MSECS_PER_SEC = 1000;
-        const int SECS_PER_MIN = 60;
-        const int MIN_PER_HOUR = 60;
-        const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
-
-        float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
-        int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
-        int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
-
-        mg_printf(connection, "%s", "Uptime: ");
-        if (hours > 0) {
-            mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
-        }
-        if (minutes > 0) {
-            mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
-        }
-        if (seconds > 0) {
-            mg_printf(connection, "%.3f seconds ", seconds);
-        }
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-
-        // display voxel file load time
-        if (theServer->isInitialLoadComplete()) {
-            time_t* loadCompleted = theServer->getLoadCompleted();
-            if (loadCompleted) {
-                tm* voxelsLoadedAtLocal = localtime(loadCompleted);
-                const int MAX_TIME_LENGTH = 128;
-                char buffer[MAX_TIME_LENGTH];
-                strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal);
-                mg_printf(connection, "Voxels Loaded At: %s", buffer);
-
-                // Convert now to tm struct for UTC
-                tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted());
-                if (gmtm != NULL) {
-                    strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM);
-                    mg_printf(connection, " [%s UTM] ", buffer);
-                }
-            } else {
-                mg_printf(connection, "%s", "Voxel Persist Disabled...\r\n");
-            }
-            mg_printf(connection, "%s", "\r\n");
-
-
-            uint64_t msecsElapsed = theServer->getLoadElapsedTime() / USECS_PER_MSEC;;
-            float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
-            int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
-            int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
-
-            mg_printf(connection, "%s", "Voxels Load Took: ");
-            if (hours > 0) {
-                mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
-            }
-            if (minutes > 0) {
-                mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
-            }
-            if (seconds >= 0) {
-                mg_printf(connection, "%.3f seconds", seconds);
-            }
-            mg_printf(connection, "%s", "\r\n");
-
-        } else {
-            mg_printf(connection, "%s", "Voxels not yet loaded...\r\n");
-        }
-
-        mg_printf(connection, "%s", "\r\n");
-
-        mg_printf(connection, "%s", "\r\n");
-
-        mg_printf(connection, "%s", "Configuration:\r\n");
-
-        for (int i = 1; i < theServer->_argc; i++) {
-            mg_printf(connection, "%s ", theServer->_argv[i]);
-        }
-        mg_printf(connection, "%s", "\r\n"); // one to end the config line
-        mg_printf(connection, "%s", "\r\n"); // two more for spacing
-        mg_printf(connection, "%s", "\r\n");
-
-        // display scene stats
-        unsigned long nodeCount = OctreeElement::getNodeCount();
-        unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
-        unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
-        
-        QLocale locale(QLocale::English);
-        const float AS_PERCENT = 100.0;
-        mg_printf(connection, "%s", "Current Nodes in scene:\r\n");
-        mg_printf(connection, "       Total Nodes: %s nodes\r\n",
-                    locale.toString((uint)nodeCount).rightJustified(16, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "    Internal Nodes: %s nodes (%5.2f%%)\r\n",
-            locale.toString((uint)internalNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
-            ((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "        Leaf Nodes: %s nodes (%5.2f%%)\r\n", 
-            locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
-            ((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-
-        // display outbound packet stats
-        mg_printf(connection, "%s", "Voxel Packet Statistics...\r\n");
-        uint64_t totalOutboundPackets = VoxelSendThread::_totalPackets;
-        uint64_t totalOutboundBytes = VoxelSendThread::_totalBytes;
-        uint64_t totalWastedBytes = VoxelSendThread::_totalWastedBytes;
-        uint64_t totalBytesOfOctalCodes = VoxelPacketData::getTotalBytesOfOctalCodes();
-        uint64_t totalBytesOfBitMasks = VoxelPacketData::getTotalBytesOfBitMasks();
-        uint64_t totalBytesOfColor = VoxelPacketData::getTotalBytesOfColor();
-
-        const int COLUMN_WIDTH = 10;
-        mg_printf(connection, "           Total Outbound Packets: %s packets\r\n",
-            locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "             Total Outbound Bytes: %s bytes\r\n",
-            locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "               Total Wasted Bytes: %s bytes\r\n",
-            locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "            Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
-            locale.toString((uint)totalBytesOfOctalCodes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
-            ((float)totalBytesOfOctalCodes / (float)totalOutboundBytes) * AS_PERCENT);
-        mg_printf(connection, "             Total BitMasks Bytes: %s bytes (%5.2f%%)\r\n",
-            locale.toString((uint)totalBytesOfBitMasks).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
-            ((float)totalBytesOfBitMasks / (float)totalOutboundBytes) * AS_PERCENT);
-        mg_printf(connection, "                Total Color Bytes: %s bytes (%5.2f%%)\r\n",
-            locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
-            ((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-        // display inbound packet stats
-        mg_printf(connection, "%s", "Voxel Edit Statistics... [RESET]\r\n");
-        uint64_t averageTransitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageTransitTimePerPacket();
-        uint64_t averageProcessTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerPacket();
-        uint64_t averageLockWaitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerPacket();
-        uint64_t averageProcessTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerVoxel();
-        uint64_t averageLockWaitTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerVoxel();
-        uint64_t totalVoxelsProcessed = theServer->_voxelServerPacketProcessor->getTotalVoxelsProcessed();
-        uint64_t totalPacketsProcessed = theServer->_voxelServerPacketProcessor->getTotalPacketsProcessed();
-
-        float averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
-
-        mg_printf(connection, "           Total Inbound Packets: %s packets\r\n",
-            locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "            Total Inbound Voxels: %s voxels\r\n",
-            locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "   Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
-        mg_printf(connection, "     Average Transit Time/Packet: %s usecs\r\n", 
-            locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "     Average Process Time/Packet: %s usecs\r\n",
-            locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "   Average Wait Lock Time/Packet: %s usecs\r\n", 
-            locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "      Average Process Time/Voxel: %s usecs\r\n",
-            locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "    Average Wait Lock Time/Voxel: %s usecs\r\n", 
-            locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-
-
-        int senderNumber = 0;
-        NodeToSenderStatsMap& allSenderStats = theServer->_voxelServerPacketProcessor->getSingleSenderStats();
-        for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
-            senderNumber++;
-            QUuid senderID = i->first;
-            SingleSenderStats& senderStats = i->second;
-
-            mg_printf(connection, "\r\n             Stats for sender %d uuid: %s\r\n", senderNumber, 
-                senderID.toString().toLocal8Bit().constData());
-
-            averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
-            averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
-            averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
-            averageProcessTimePerVoxel = senderStats.getAverageProcessTimePerVoxel();
-            averageLockWaitTimePerVoxel = senderStats.getAverageLockWaitTimePerVoxel();
-            totalVoxelsProcessed = senderStats.getTotalVoxelsProcessed();
-            totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
-
-            averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
-
-            mg_printf(connection, "               Total Inbound Packets: %s packets\r\n",
-                locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "                Total Inbound Voxels: %s voxels\r\n",
-                locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "       Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
-            mg_printf(connection, "         Average Transit Time/Packet: %s usecs\r\n", 
-                locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "         Average Process Time/Packet: %s usecs\r\n",
-                locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "       Average Wait Lock Time/Packet: %s usecs\r\n", 
-                locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "          Average Process Time/Voxel: %s usecs\r\n",
-                locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "        Average Wait Lock Time/Voxel: %s usecs\r\n", 
-                locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-
-        }
-
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-        // display memory usage stats
-        mg_printf(connection, "%s", "Current Memory Usage Statistics\r\n");
-        mg_printf(connection, "\r\nVoxelTreeElement size... %ld bytes\r\n", sizeof(VoxelTreeElement));
-        mg_printf(connection, "%s", "\r\n");
-
-        const char* memoryScaleLabel;
-        const float MEGABYTES = 1000000.f;
-        const float GIGABYTES = 1000000000.f;
-        float memoryScale;
-        if (OctreeElement::getTotalMemoryUsage() / MEGABYTES < 1000.0f) {
-            memoryScaleLabel = "MB";
-            memoryScale = MEGABYTES;
-        } else {
-            memoryScaleLabel = "GB";
-            memoryScale = GIGABYTES;
-        }
-
-        mg_printf(connection, "Voxel Node Memory Usage:         %8.2f %s\r\n", 
-            OctreeElement::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
-        mg_printf(connection, "Octcode Memory Usage:            %8.2f %s\r\n", 
-            OctreeElement::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
-        mg_printf(connection, "External Children Memory Usage:  %8.2f %s\r\n", 
-            OctreeElement::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
-        mg_printf(connection, "%s", "                                 -----------\r\n");
-        mg_printf(connection, "                         Total:  %8.2f %s\r\n", 
-            OctreeElement::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "OctreeElement Children Population Statistics...\r\n");
-        checkSum = 0;
-        for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
-            checkSum += OctreeElement::getChildrenCount(i);
-            mg_printf(connection, "    Nodes with %d children:      %s nodes (%5.2f%%)\r\n", i, 
-                locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
-                ((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
-        }
-        mg_printf(connection, "%s", "                                ----------------------\r\n");
-        mg_printf(connection, "                    Total:      %s nodes\r\n", 
-            locale.toString((uint)checkSum).rightJustified(16, ' ').toLocal8Bit().constData());
-
-#ifdef BLENDED_UNION_CHILDREN
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "OctreeElement Children Encoding Statistics...\r\n");
-        
-        mg_printf(connection, "    Single or No Children:      %10.llu nodes (%5.2f%%)\r\n",
-            OctreeElement::getSingleChildrenCount(), ((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Two Children as Offset:     %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getTwoChildrenOffsetCount(), 
-            ((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Two Children as External:   %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getTwoChildrenExternalCount(), 
-            ((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Three Children as Offset:   %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getThreeChildrenOffsetCount(), 
-            ((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Three Children as External: %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getThreeChildrenExternalCount(), 
-            ((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
-            OctreeElement::getExternalChildrenCount(), 
-            ((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
-
-        checkSum = OctreeElement::getSingleChildrenCount() +
-                            OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() + 
-                            OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() + 
-                            OctreeElement::getExternalChildrenCount();
-
-        mg_printf(connection, "%s", "                                ----------------\r\n");
-        mg_printf(connection, "                         Total: %10.llu nodes\r\n", checkSum);
-        mg_printf(connection, "                      Expected: %10.lu nodes\r\n", nodeCount);
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "In other news....\r\n");
-        mg_printf(connection, "could store 4 children internally:     %10.llu nodes\r\n",
-            OctreeElement::getCouldStoreFourChildrenInternally());
-        mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n", 
-            OctreeElement::getCouldNotStoreFourChildrenInternally());
-#endif
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "
\r\n"); - - mg_printf(connection, "%s", "
"); - - return 1; - } else { - // have mongoose process this request from the document_root - return 0; - } + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + return envPacketLength; } -void VoxelServer::setArguments(int argc, char** argv) { - _argc = argc; - _argv = const_cast(argv); - - qDebug("VoxelServer::setArguments()\n"); - for (int i = 0; i < _argc; i++) { - qDebug("_argv[%d]=%s\n", i, _argv[i]); - } - -} - -void VoxelServer::parsePayload() { - - if (getNumPayloadBytes() > 0) { - QString config((const char*) _payload); - - // Now, parse the config - QStringList configList = config.split(" "); - - int argCount = configList.size() + 1; - - qDebug("VoxelServer::parsePayload()... argCount=%d\n",argCount); - - _parsedArgV = new char*[argCount]; - const char* dummy = "config-from-payload"; - _parsedArgV[0] = new char[strlen(dummy) + sizeof(char)]; - strcpy(_parsedArgV[0], dummy); - - for (int i = 1; i < argCount; i++) { - QString configItem = configList.at(i-1); - _parsedArgV[i] = new char[configItem.length() + sizeof(char)]; - strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData()); - qDebug("VoxelServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]); - } - - setArguments(argCount, _parsedArgV); - } -} - -void VoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { - NodeList* nodeList = NodeList::getInstance(); - - if (dataByteArray[0] == PACKET_TYPE_VOXEL_QUERY) { - bool debug = false; - if (debug) { - qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow()); - } - - int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data()); - - // If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we - // need to make sure we have it in our nodeList. - QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesPacketHeader, - NUM_BYTES_RFC4122_UUID)); - - Node* node = nodeList->nodeWithUUID(nodeUUID); - - if (node) { - nodeList->updateNodeWithData(node, senderSockAddr, (unsigned char *) dataByteArray.data(), - dataByteArray.size()); - if (!node->getActiveSocket()) { - // we don't have an active socket for this node, but they're talking to us - // this means they've heard from us and can reply, let's assume public is active - node->activatePublicSocket(); - } - VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); - if (nodeData && !nodeData->isVoxelSendThreadInitalized()) { - nodeData->initializeVoxelSendThread(this); - } - } - } else if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { - if (_jurisdictionSender) { - _jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), - dataByteArray.size()); - } - } else if (_voxelServerPacketProcessor && - (dataByteArray[0] == PACKET_TYPE_SET_VOXEL - || dataByteArray[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE - || dataByteArray[0] == PACKET_TYPE_ERASE_VOXEL)) { - - - const char* messageName; - switch (dataByteArray[0]) { - case PACKET_TYPE_SET_VOXEL: - messageName = "PACKET_TYPE_SET_VOXEL"; - break; - case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: - messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE"; - break; - case PACKET_TYPE_ERASE_VOXEL: - messageName = "PACKET_TYPE_ERASE_VOXEL"; - break; - } - _voxelServerPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), - dataByteArray.size()); - } else { - // let processNodeData handle it. - NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), - dataByteArray.size()); - } -} - -//int main(int argc, const char * argv[]) { -void VoxelServer::run() { - - const char VOXEL_SERVER_LOGGING_TARGET_NAME[] = "voxel-server"; - - // change the logging target name while this is running - Logging::setTargetName(VOXEL_SERVER_LOGGING_TARGET_NAME); - - // Now would be a good time to parse our arguments, if we got them as assignment - if (getNumPayloadBytes() > 0) { - parsePayload(); - } - - qInstallMessageHandler(Logging::verboseMessageHandler); - - const char* STATUS_PORT = "--statusPort"; - const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT); - if (statusPort) { - int statusPortNumber = atoi(statusPort); - initMongoose(statusPortNumber); - } - - - const char* JURISDICTION_FILE = "--jurisdictionFile"; - const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE); - if (jurisdictionFile) { - qDebug("jurisdictionFile=%s\n", jurisdictionFile); - - qDebug("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); - _jurisdiction = new JurisdictionMap(jurisdictionFile); - qDebug("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); - } else { - const char* JURISDICTION_ROOT = "--jurisdictionRoot"; - const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT); - if (jurisdictionRoot) { - qDebug("jurisdictionRoot=%s\n", jurisdictionRoot); - } - - const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes"; - const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES); - if (jurisdictionEndNodes) { - qDebug("jurisdictionEndNodes=%s\n", jurisdictionEndNodes); - } - - if (jurisdictionRoot || jurisdictionEndNodes) { - _jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); - } - } - - // should we send environments? Default is yes, but this command line suppresses sending - const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove"; - _dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE); - qDebug("dumpVoxelsOnMove=%s\n", debug::valueOf(_dumpVoxelsOnMove)); - +void VoxelServer::beforeRun() { // should we send environments? Default is yes, but this command line suppresses sending const char* SEND_ENVIRONMENTS = "--sendEnvironments"; bool dontSendEnvironments = !cmdOptionExists(_argc, _argv, SEND_ENVIRONMENTS); if (dontSendEnvironments) { qDebug("Sending environments suppressed...\n"); _sendEnvironments = false; - } else { + } else { + _sendEnvironments = true; // should we send environments? Default is yes, but this command line suppresses sending const char* MINIMAL_ENVIRONMENT = "--minimalEnvironment"; _sendMinimalEnvironment = cmdOptionExists(_argc, _argv, MINIMAL_ENVIRONMENT); qDebug("Using Minimal Environment=%s\n", debug::valueOf(_sendMinimalEnvironment)); } qDebug("Sending environments=%s\n", debug::valueOf(_sendEnvironments)); - - NodeList* nodeList = NodeList::getInstance(); - nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER); - - // we need to ask the DS about agents so we can ping/reply with them - const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER}; - nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); - - setvbuf(stdout, NULL, _IOLBF, 0); - - // tell our NodeList about our desire to get notifications - nodeList->addHook(&_nodeWatcher); - nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode; - - nodeList->startSilentNodeRemovalThread(); - srand((unsigned)time(0)); - - const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats"; - _displayVoxelStats = cmdOptionExists(_argc, _argv, DISPLAY_VOXEL_STATS); - qDebug("displayVoxelStats=%s\n", debug::valueOf(_displayVoxelStats)); - - const char* VERBOSE_DEBUG = "--verboseDebug"; - _verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG); - qDebug("verboseDebug=%s\n", debug::valueOf(_verboseDebug)); - - const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending"; - _debugVoxelSending = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_SENDING); - qDebug("debugVoxelSending=%s\n", debug::valueOf(_debugVoxelSending)); - - const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving"; - _debugVoxelReceiving = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_RECEIVING); - qDebug("debugVoxelReceiving=%s\n", debug::valueOf(_debugVoxelReceiving)); - - const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug"; - _shouldShowAnimationDebug = cmdOptionExists(_argc, _argv, WANT_ANIMATION_DEBUG); - qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug)); - - // By default we will voxel persist, if you want to disable this, then pass in this parameter - const char* NO_VOXEL_PERSIST = "--NoVoxelPersist"; - if (cmdOptionExists(_argc, _argv, NO_VOXEL_PERSIST)) { - _wantVoxelPersist = false; - } - qDebug("wantVoxelPersist=%s\n", debug::valueOf(_wantVoxelPersist)); - - // if we want Voxel Persistence, set up the local file and persist thread - if (_wantVoxelPersist) { - - // Check to see if the user passed in a command line option for setting packet send rate - const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename"; - const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME); - if (voxelsPersistFilenameParameter) { - strcpy(_voxelPersistFilename, voxelsPersistFilenameParameter); - } else { - //strcpy(voxelPersistFilename, _wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); - strcpy(_voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE); - } - - qDebug("voxelPersistFilename=%s\n", _voxelPersistFilename); - - // now set up VoxelPersistThread - _voxelPersistThread = new VoxelPersistThread(&_serverTree, _voxelPersistFilename); - if (_voxelPersistThread) { - _voxelPersistThread->initialize(true); - } - } - - // Check to see if the user passed in a command line option for loading an old style local - // Voxel File. If so, load it now. This is not the same as a voxel persist file - const char* INPUT_FILE = "-i"; - const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE); - if (voxelsFilename) { - _serverTree.readFromSVOFile(voxelsFilename); - } - - // Check to see if the user passed in a command line option for setting packet send rate - const char* PACKETS_PER_SECOND = "--packetsPerSecond"; - const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND); - if (packetsPerSecond) { - _packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND; - if (_packetsPerClientPerInterval < 1) { - _packetsPerClientPerInterval = 1; - } - qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval); - } - - HifiSockAddr senderSockAddr; - - // set up our jurisdiction broadcaster... - _jurisdictionSender = new JurisdictionSender(_jurisdiction); - if (_jurisdictionSender) { - _jurisdictionSender->initialize(true); - } - - // set up our VoxelServerPacketProcessor - _voxelServerPacketProcessor = new VoxelServerPacketProcessor(this); - if (_voxelServerPacketProcessor) { - _voxelServerPacketProcessor->initialize(true); - } - - // Convert now to tm struct for local timezone - tm* localtm = localtime(&_started); - const int MAX_TIME_LENGTH = 128; - char localBuffer[MAX_TIME_LENGTH] = { 0 }; - char utcBuffer[MAX_TIME_LENGTH] = { 0 }; - strftime(localBuffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm); - // Convert now to tm struct for UTC - tm* gmtm = gmtime(&_started); - if (gmtm != NULL) { - strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm); - } - qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n"; - - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - - // loop to send to nodes requesting data - while (!_isFinished) { - QCoreApplication::processEvents(); - } - - // call NodeList::clear() so that all of our node specific objects, including our sending threads, are - // properly shutdown and cleaned up. - NodeList::getInstance()->clear(); - - if (_jurisdictionSender) { - _jurisdictionSender->terminate(); - delete _jurisdictionSender; - } - - if (_voxelServerPacketProcessor) { - _voxelServerPacketProcessor->terminate(); - delete _voxelServerPacketProcessor; - } - - if (_voxelPersistThread) { - _voxelPersistThread->terminate(); - delete _voxelPersistThread; - } - - // tell our NodeList we're done with notifications - nodeList->removeHook(&_nodeWatcher); - - delete _jurisdiction; - _jurisdiction = NULL; - - qDebug() << "VoxelServer::run()... DONE\n"; } - -**/ - diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index 3c20cf6d9a..aaaa68c01e 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -40,11 +40,17 @@ public: virtual unsigned char getMyNodeType(); virtual const char* getMyLoggingServerTargetName(); virtual const char* getMyDefaultPersistFilename(); + virtual bool hasSpecialPacketToSend(); + virtual int sendSpecialPacket(Node* node); + + // subclass may implement these method + virtual void beforeRun(); private: bool _sendEnvironments; bool _sendMinimalEnvironment; EnvironmentData _environmentData[3]; + unsigned char _tempOutputBuffer[MAX_PACKET_SIZE]; }; #endif // __voxel_server__VoxelServer__