diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index f0d23cac62..ff876596b0 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -22,6 +22,7 @@ #include #include + #include "AssignmentFactory.h" #include "AssignmentThread.h" @@ -30,11 +31,12 @@ const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client"; const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000; +SharedAssignmentPointer AssignmentClient::_currentAssignment; + int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), - _currentAssignment(), _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) { DTLSClientSession::globalInit(); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 89fe74f044..2df9f4ca40 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -20,15 +20,17 @@ class AssignmentClient : public QCoreApplication { Q_OBJECT public: AssignmentClient(int &argc, char **argv); + static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } ~AssignmentClient(); private slots: void sendAssignmentRequest(); void readPendingDatagrams(); void assignmentCompleted(); void handleAuthenticationRequest(); + private: Assignment _requestAssignment; - SharedAssignmentPointer _currentAssignment; + static SharedAssignmentPointer _currentAssignment; QString _assignmentServerHostname; }; diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 00526cc967..10d30ad1ae 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -47,41 +47,54 @@ OctreeQueryNode::OctreeQueryNode() : OctreeQueryNode::~OctreeQueryNode() { _isShuttingDown = true; - const bool extraDebugging = false; - if (extraDebugging) { - qDebug() << "OctreeQueryNode::~OctreeQueryNode()"; - } if (_octreeSendThread) { - if (extraDebugging) { - qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling _octreeSendThread->terminate()"; - } - _octreeSendThread->terminate(); - if (extraDebugging) { - qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling delete _octreeSendThread"; - } - delete _octreeSendThread; + forceNodeShutdown(); } - + delete[] _octreePacket; delete[] _lastOctreePacket; - if (extraDebugging) { - qDebug() << "OctreeQueryNode::~OctreeQueryNode()... DONE..."; - } } - -void OctreeQueryNode::deleteLater() { +void OctreeQueryNode::nodeKilled() { _isShuttingDown = true; + nodeBag.unhookNotifications(); // if our node is shutting down, then we no longer need octree element notifications if (_octreeSendThread) { + // just tell our thread we want to shutdown, this is asynchronous, and fast, we don't need or want it to block + // while the thread actually shuts down _octreeSendThread->setIsShuttingDown(); } - OctreeQuery::deleteLater(); } +void OctreeQueryNode::forceNodeShutdown() { + _isShuttingDown = true; + nodeBag.unhookNotifications(); // if our node is shutting down, then we no longer need octree element notifications + if (_octreeSendThread) { + // we really need to force our thread to shutdown, this is synchronous, we will block while the thread actually + // shuts down because we really need it to shutdown, and it's ok if we wait for it to complete + OctreeSendThread* sendThread = _octreeSendThread; + _octreeSendThread = NULL; + sendThread->setIsShuttingDown(); + sendThread->terminate(); + delete sendThread; + } +} -void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node) { - // Create octree sending thread... - _octreeSendThread = new OctreeSendThread(octreeServer, node); +void OctreeQueryNode::sendThreadFinished() { + // We've been notified by our thread that it is shutting down. So we can clean up our reference to it, and + // delete the actual thread object. Cleaning up our thread will correctly unroll all refereces to shared + // pointers to our node as well as the octree server assignment + if (_octreeSendThread) { + OctreeSendThread* sendThread = _octreeSendThread; + _octreeSendThread = NULL; + delete sendThread; + } +} + +void OctreeQueryNode::initializeOctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node) { + _octreeSendThread = new OctreeSendThread(myAssignment, node); + + // we want to be notified when the thread finishes + connect(_octreeSendThread, &GenericThread::finished, this, &OctreeQueryNode::sendThreadFinished); _octreeSendThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index aa445db8a6..7b42208f16 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -13,25 +13,25 @@ #define hifi_OctreeQueryNode_h #include -#include -#include -#include + #include +#include #include #include +#include +#include #include +#include // for SharedAssignmentPointer class OctreeSendThread; -class OctreeServer; class OctreeQueryNode : public OctreeQuery { Q_OBJECT public: OctreeQueryNode(); virtual ~OctreeQueryNode(); - virtual void deleteLater(); - + void init(); // called after creation to set up some virtual items virtual PacketType getMyPacketType() const = 0; @@ -86,7 +86,7 @@ public: OctreeSceneStats stats; - void initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node); + void initializeOctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node); bool isOctreeSendThreadInitalized() { return _octreeSendThread; } void dumpOutOfView(); @@ -96,8 +96,13 @@ public: unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; } int getDuplicatePacketCount() const { return _duplicatePacketCount; } + void nodeKilled(); + void forceNodeShutdown(); bool isShuttingDown() const { return _isShuttingDown; } +private slots: + void sendThreadFinished(); + private: OctreeQueryNode(const OctreeQueryNode &); OctreeQueryNode& operator= (const OctreeQueryNode&); diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 0a53160b8f..d8a9f3d1ea 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -23,12 +23,13 @@ quint64 startSceneSleepTime = 0; quint64 endSceneSleepTime = 0; -OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) : - _myServer(myServer), +OctreeSendThread::OctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node) : + _myAssignment(myAssignment), + _myServer(static_cast(myAssignment.data())), + _node(node), _nodeUUID(node->getUUID()), _packetData(), _nodeMissingCount(0), - _processLock(), _isShuttingDown(false) { QString safeServerName("Octree"); @@ -41,22 +42,24 @@ OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer nod OctreeServer::clientConnected(); } -OctreeSendThread::~OctreeSendThread() { +OctreeSendThread::~OctreeSendThread() { QString safeServerName("Octree"); if (_myServer) { safeServerName = _myServer->getMyServerName(); } + qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; + OctreeServer::clientDisconnected(); + OctreeServer::stopTrackingThread(this); + + _node.clear(); + _myAssignment.clear(); } void OctreeSendThread::setIsShuttingDown() { _isShuttingDown = true; - OctreeServer::stopTrackingThread(this); - - // this will cause us to wait till the process loop is complete, we do this after we change _isShuttingDown - QMutexLocker locker(&_processLock); } @@ -65,47 +68,29 @@ bool OctreeSendThread::process() { return false; // exit early if we're shutting down } + // check that our server and assignment is still valid + if (!_myServer || !_myAssignment) { + return false; // exit early if it's not, it means the server is shutting down + } + OctreeServer::didProcess(this); - float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; - quint64 lockWaitStart = usecTimestampNow(); - _processLock.lock(); - quint64 lockWaitEnd = usecTimestampNow(); - lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); - OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec); - quint64 start = usecTimestampNow(); // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { - SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false); - if (node) { + if (_node) { _nodeMissingCount = 0; - OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); + OctreeQueryNode* nodeData = static_cast(_node->getLinkedData()); // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - packetDistributor(node, nodeData, viewFrustumChanged); - } - } else { - _nodeMissingCount++; - const int MANY_FAILED_LOCKS = 1; - if (_nodeMissingCount >= MANY_FAILED_LOCKS) { - - QString safeServerName("Octree"); - if (_myServer) { - safeServerName = _myServer->getMyServerName(); - } - - qDebug() << qPrintable(safeServerName) << "server: sending thread [" << this << "]" - << "failed to get nodeWithUUID() " << _nodeUUID <<". Failed:" << _nodeMissingCount << "times"; + packetDistributor(nodeData, viewFrustumChanged); } } } - _processLock.unlock(); - if (_isShuttingDown) { return false; // exit early if we're shutting down } @@ -135,8 +120,7 @@ quint64 OctreeSendThread::_totalBytes = 0; quint64 OctreeSendThread::_totalWastedBytes = 0; quint64 OctreeSendThread::_totalPackets = 0; -int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, - OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { +int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { OctreeServer::didHandlePacketSend(this); @@ -196,12 +180,12 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, // actually send it OctreeServer::didCallWriteDatagram(this); - NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node)); + NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, _node); packetSent = true; } else { // not enough room in the packet, send two packets OctreeServer::didCallWriteDatagram(this); - NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node)); + NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, _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. @@ -220,8 +204,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, packetsSent++; OctreeServer::didCallWriteDatagram(this); - NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), - SharedNodePointer(node)); + NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), _node); packetSent = true; @@ -241,8 +224,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) { // just send the voxel packet OctreeServer::didCallWriteDatagram(this); - NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), - SharedNodePointer(node)); + NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), _node); packetSent = true; int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); @@ -269,7 +251,8 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, } /// Version of voxel distributor that sends the deepest LOD level at once -int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { +int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) { + OctreeServer::didPacketDistributor(this); // if shutting down, exit early @@ -299,7 +282,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // then let's just send that waiting packet. if (!nodeData->getCurrentPacketFormatMatches()) { if (nodeData->isPacketWaiting()) { - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); } else { nodeData->resetOctreePacket(); } @@ -340,7 +323,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue //unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); //unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + int packetsJustSent = handlePacketSend(nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval += packetsJustSent; // If we're starting a full scene, then definitely we want to empty the nodeBag @@ -491,7 +474,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue if (writtenSize > nodeData->getAvailable()) { - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); } nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); @@ -513,7 +496,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (sendNow) { quint64 packetSendingStart = usecTimestampNow(); - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); quint64 packetSendingEnd = usecTimestampNow(); packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart); @@ -546,8 +529,8 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // Here's where we can/should allow the server to send other data... // send the environment packet // TODO: should we turn this into a while loop to better handle sending multiple special packets - if (_myServer->hasSpecialPacketToSend(node) && !nodeData->isShuttingDown()) { - trueBytesSent += _myServer->sendSpecialPacket(node); + if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) { + trueBytesSent += _myServer->sendSpecialPacket(_node); truePacketsSent++; packetsSentThisInterval++; } diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 95205af32f..d8eed27802 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -17,15 +17,16 @@ #include #include #include -#include "OctreeQueryNode.h" -#include "OctreeServer.h" +#include "OctreeQueryNode.h" + +class OctreeServer; /// Threaded processor for sending voxel packets to a single client class OctreeSendThread : public GenericThread { Q_OBJECT public: - OctreeSendThread(OctreeServer* myServer, SharedNodePointer node); + OctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node); virtual ~OctreeSendThread(); void setIsShuttingDown(); @@ -42,16 +43,17 @@ protected: virtual bool process(); private: + SharedAssignmentPointer _myAssignment; OctreeServer* _myServer; + SharedNodePointer _node; QUuid _nodeUUID; - int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); - int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged); + int handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); + int packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged); OctreePacketData _packetData; int _nodeMissingCount; - QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing bool _isShuttingDown; }; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 2e8a354c6a..bd04dd85d7 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -19,6 +19,8 @@ #include #include +#include "../AssignmentClient.h" + #include "OctreeServer.h" #include "OctreeServerConsts.h" @@ -206,7 +208,7 @@ void OctreeServer::trackProcessWaitTime(float time) { } void OctreeServer::attachQueryNodeToNode(Node* newNode) { - if (!newNode->getLinkedData()) { + if (!newNode->getLinkedData() && _instance) { OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); newQueryNodeData->init(); newNode->setLinkedData(newQueryNodeData); @@ -234,7 +236,13 @@ OctreeServer::OctreeServer(const QByteArray& packet) : _started(time(0)), _startedUSecs(usecTimestampNow()) { + if (_instance) { + qDebug() << "Octree Server starting... while old instance still running _instance=["<<_instance<<"] this=[" << this << "]"; + } + + qDebug() << "Octree Server starting... setting _instance to=[" << this << "]"; _instance = this; + _averageLoopTime.updateAverage(0); qDebug() << "Octree server starting... [" << this << "]"; } @@ -265,6 +273,16 @@ OctreeServer::~OctreeServer() { delete _jurisdiction; _jurisdiction = NULL; + + // cleanup our tree here... + qDebug() << qPrintable(_safeServerName) << "server START cleaning up octree... [" << this << "]"; + delete _tree; + _tree = NULL; + qDebug() << qPrintable(_safeServerName) << "server DONE cleaning up octree... [" << this << "]"; + + if (_instance == this) { + _instance = NULL; // we are gone + } qDebug() << qPrintable(_safeServerName) << "server DONE shutting down... [" << this << "]"; } @@ -812,33 +830,22 @@ void OctreeServer::readPendingDatagrams() { while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { PacketType packetType = packetTypeForPacket(receivedPacket); - SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket); - if (packetType == getMyQueryMessageType()) { - bool debug = false; - if (debug) { - if (matchingNode) { - qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node:" << *matchingNode; - } else { - qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node: ??????"; - } - } - + // If we got a PacketType_VOXEL_QUERY, then we're talking to an NodeType_t_AVATAR, and we // need to make sure we have it in our nodeList. if (matchingNode) { - if (debug) { - qDebug() << "calling updateNodeWithDataFromPacket()... node:" << *matchingNode; - } nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket); - OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData(); if (nodeData && !nodeData->isOctreeSendThreadInitalized()) { - if (debug) { - qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode; - } - nodeData->initializeOctreeSendThread(this, matchingNode); + + // NOTE: this is an important aspect of the proper ref counting. The send threads/node data need to + // know that the OctreeServer/Assignment will not get deleted on it while it's still active. The + // solution is to get the shared pointer for the current assignment. We need to make sure this is the + // same SharedAssignmentPointer that was ref counted by the assignment client. + SharedAssignmentPointer sharedAssignment = AssignmentClient::getCurrentAssignment(); + nodeData->initializeOctreeSendThread(sharedAssignment, matchingNode); } } } else if (packetType == PacketTypeJurisdictionRequest) { @@ -1042,23 +1049,46 @@ void OctreeServer::nodeAdded(SharedNodePointer node) { } void OctreeServer::nodeKilled(SharedNodePointer node) { + quint64 start = usecTimestampNow(); + qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node; OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); if (nodeData) { - qDebug() << qPrintable(_safeServerName) << "server resetting Linked Data for node:" << *node; - node->setLinkedData(NULL); // set this first in case another thread comes through and tryes to acces this - qDebug() << qPrintable(_safeServerName) << "server deleting Linked Data for node:" << *node; - nodeData->deleteLater(); + nodeData->nodeKilled(); // tell our node data and sending threads that we'd like to shut down } else { qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node; } + + quint64 end = usecTimestampNow(); + quint64 usecsElapsed = (end - start); + if (usecsElapsed > 1000) { + qDebug() << qPrintable(_safeServerName) << "server nodeKilled() took: " << usecsElapsed << " usecs for node:" << *node; + } } +void OctreeServer::forceNodeShutdown(SharedNodePointer node) { + quint64 start = usecTimestampNow(); + + qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node; + OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); + if (nodeData) { + nodeData->forceNodeShutdown(); // tell our node data and sending threads that we'd like to shut down + } else { + qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node; + } + + quint64 end = usecTimestampNow(); + quint64 usecsElapsed = (end - start); + qDebug() << qPrintable(_safeServerName) << "server forceNodeShutdown() took: " + << usecsElapsed << " usecs for node:" << *node; +} + + void OctreeServer::aboutToFinish() { qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish..."; foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node; - nodeKilled(node); + forceNodeShutdown(node); } qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish..."; } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index d02764bc59..d7139b5c3d 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -117,6 +117,7 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url); virtual void aboutToFinish(); + void forceNodeShutdown(SharedNodePointer node); public slots: /// runs the voxel server assignment diff --git a/examples/testingVoxelViewerRestart.js b/examples/testingVoxelViewerRestart.js new file mode 100644 index 0000000000..4fbdee1223 --- /dev/null +++ b/examples/testingVoxelViewerRestart.js @@ -0,0 +1,93 @@ +// +// testingVoxelSeeingRestart.js +// hifi +// +// Created by Brad Hefta-Gaub on 2/26/14 +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script +// + +var count = 0; +var yawDirection = -1; +var yaw = 45; +var yawMax = 70; +var yawMin = 20; +var vantagePoint = {x: 5000, y: 500, z: 5000}; + +var isLocal = false; + +// set up our VoxelViewer with a position and orientation +var orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0); + +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function init() { + if (isLocal) { + MyAvatar.position = vantagePoint; + MyAvatar.orientation = orientation; + } else { + VoxelViewer.setPosition(vantagePoint); + VoxelViewer.setOrientation(orientation); + VoxelViewer.queryOctree(); + Agent.isAvatar = true; + } +} + +function keepLooking(deltaTime) { + //print("count =" + count); + + if (count == 0) { + init(); + } + count++; + if (count % getRandomInt(5, 15) == 0) { + yaw += yawDirection; + orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0); + if (yaw > yawMax || yaw < yawMin) { + yawDirection = yawDirection * -1; + } + + //if (count % 10000 == 0) { + // print("calling VoxelViewer.queryOctree()... count=" + count + " yaw=" + yaw); + //} + + if (isLocal) { + MyAvatar.orientation = orientation; + } else { + VoxelViewer.setOrientation(orientation); + VoxelViewer.queryOctree(); + + //if (count % 10000 == 0) { + // print("VoxelViewer.getOctreeElementsCount()=" + VoxelViewer.getOctreeElementsCount()); + //} + } + } + + // approximately every second, consider stopping + if (count % 60 == 0) { + print("considering stop.... elementCount:" + VoxelViewer.getOctreeElementsCount()); + var stopProbability = 0.05; // 5% chance of stopping + if (Math.random() < stopProbability) { + print("stopping.... elementCount:" + VoxelViewer.getOctreeElementsCount()); + Script.stop(); + } + } +} + +function scriptEnding() { + print("SCRIPT ENDNG!!!\n"); +} + +// register the call back so it fires before each data send +Script.update.connect(keepLooking); + +// register our scriptEnding callback +Script.scriptEnding.connect(scriptEnding); + + +// test for local... +Menu.isOptionChecked("Voxels"); +isLocal = true; // will only get here on local client diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b95d3e4b38..f461b7e44c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2214,7 +2214,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node int packetLength = endOfQueryPacket - queryPacket; // make sure we still have an active socket - nodeList->writeDatagram(reinterpret_cast(queryPacket), packetLength, node); + nodeList->writeUnverifiedDatagram(reinterpret_cast(queryPacket), packetLength, node); // Feed number of bytes to corresponding channel of the bandwidth meter _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(packetLength); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index db8a689001..ce78ec2d10 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -227,7 +227,30 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const SharedNo } } - writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret()); + return writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret()); + } + + // didn't have a destinationNode to send to, return 0 + return 0; +} + +qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode, + const HifiSockAddr& overridenSockAddr) { + if (destinationNode) { + // if we don't have an ovveriden address, assume they want to send to the node's active socket + const HifiSockAddr* destinationSockAddr = &overridenSockAddr; + if (overridenSockAddr.isNull()) { + if (destinationNode->getActiveSocket()) { + // use the node's active socket as the destination socket + destinationSockAddr = destinationNode->getActiveSocket(); + } else { + // we don't have a socket to send to, return 0 + return 0; + } + } + + // don't use the node secret! + return writeDatagram(datagram, *destinationSockAddr, QUuid()); } // didn't have a destinationNode to send to, return 0 @@ -243,6 +266,11 @@ qint64 LimitedNodeList::writeDatagram(const char* data, qint64 size, const Share return writeDatagram(QByteArray(data, size), destinationNode, overridenSockAddr); } +qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode, + const HifiSockAddr& overridenSockAddr) { + return writeUnverifiedDatagram(QByteArray(data, size), destinationNode, overridenSockAddr); +} + void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) { // the node decided not to do anything with this packet // if it comes from a known source we should keep that node alive diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index ea9cb42436..2a98dc536f 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -66,10 +66,17 @@ public: qint64 writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode, const HifiSockAddr& overridenSockAddr = HifiSockAddr()); + + qint64 writeUnverifiedDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode, + const HifiSockAddr& overridenSockAddr = HifiSockAddr()); + qint64 writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr); qint64 writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode, const HifiSockAddr& overridenSockAddr = HifiSockAddr()); + qint64 writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode, + const HifiSockAddr& overridenSockAddr = HifiSockAddr()); + void(*linkedDataCreateCallback)(Node *); NodeHash getNodeHash(); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 0f52f90962..b7535e5064 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -69,7 +69,7 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse - << PacketTypeNodeJsonStats; + << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery; const int NUM_BYTES_MD5_HASH = 16; const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; diff --git a/libraries/octree/src/OctreeElementBag.cpp b/libraries/octree/src/OctreeElementBag.cpp index f929980a75..92a8fe5bff 100644 --- a/libraries/octree/src/OctreeElementBag.cpp +++ b/libraries/octree/src/OctreeElementBag.cpp @@ -16,13 +16,21 @@ OctreeElementBag::OctreeElementBag() : _bagElements() { OctreeElement::addDeleteHook(this); + _hooked = true; }; OctreeElementBag::~OctreeElementBag() { - OctreeElement::removeDeleteHook(this); + unhookNotifications(); deleteAll(); } +void OctreeElementBag::unhookNotifications() { + if (_hooked) { + OctreeElement::removeDeleteHook(this); + _hooked = false; + } +} + void OctreeElementBag::elementDeleted(OctreeElement* element) { remove(element); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains() } diff --git a/libraries/octree/src/OctreeElementBag.h b/libraries/octree/src/OctreeElementBag.h index afc34bf1a6..8c18ece773 100644 --- a/libraries/octree/src/OctreeElementBag.h +++ b/libraries/octree/src/OctreeElementBag.h @@ -36,8 +36,11 @@ public: void deleteAll(); virtual void elementDeleted(OctreeElement* element); + void unhookNotifications(); + private: QSet _bagElements; + bool _hooked; }; #endif // hifi_OctreeElementBag_h diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index b25cb4ff8a..5574b376cb 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -221,7 +221,7 @@ void OctreeHeadlessViewer::queryOctree() { int packetLength = endOfQueryPacket - queryPacket; // make sure we still have an active socket - nodeList->writeDatagram(reinterpret_cast(queryPacket), packetLength, node); + nodeList->writeUnverifiedDatagram(reinterpret_cast(queryPacket), packetLength, node); } } } diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index f2d19a66ea..c2e38b73c9 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -130,9 +130,11 @@ void ViewFrustum::calculate() { // Also calculate our projection matrix in case people want to project points... // Projection matrix : Field of View, ratio, display range : near to far - glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, _farClip); - glm::vec3 lookAt = _position + _direction; - glm::mat4 view = glm::lookAt(_position, lookAt, _up); + const float CLIP_NUDGE = 1.0f; + float farClip = (_farClip != _nearClip) ? _farClip : _nearClip + CLIP_NUDGE; // don't allow near and far to be equal + glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, farClip); + glm::vec3 lookAt = _position + _direction; + glm::mat4 view = glm::lookAt(_position, lookAt, _up); // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) _ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around