diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f43bbc3af..c533f360f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,5 +17,4 @@ add_subdirectory(assignment-client) add_subdirectory(domain-server) add_subdirectory(interface) add_subdirectory(pairing-server) -add_subdirectory(space-server) add_subdirectory(voxel-edit) \ No newline at end of file diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp index e9e8a9857d..a77be03aa5 100644 --- a/animation-server/src/main.cpp +++ b/animation-server/src/main.cpp @@ -841,7 +841,7 @@ int main(int argc, const char * argv[]) pthread_t animateVoxelThread; pthread_create(&animateVoxelThread, NULL, animateVoxels, NULL); - sockaddr nodePublicAddress; + HifiSockAddr nodeSockAddr; unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; ssize_t receivedBytes; @@ -858,15 +858,18 @@ int main(int argc, const char * argv[]) } // Nodes sending messages to us... - if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes) && + if (nodeList->getNodeSocket().hasPendingDatagrams() + && (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, + nodeSockAddr.getAddressPointer(), + nodeSockAddr.getPortPointer())) && packetVersionMatch(packetData)) { if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { if (::jurisdictionListener) { - ::jurisdictionListener->queueReceivedPacket(nodePublicAddress, packetData, receivedBytes); + ::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes); } } - NodeList::getInstance()->processNodeData(&nodePublicAddress, packetData, receivedBytes); + NodeList::getInstance()->processNodeData(nodeSockAddr, packetData, receivedBytes); } } diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4319ca706e..95c3f91311 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -6,7 +6,12 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // -#include +#include +#include +#include +#include +#include +#include #include #include @@ -14,31 +19,12 @@ #include #include "Agent.h" -#include "voxels/VoxelScriptingInterface.h" Agent::Agent(const unsigned char* dataBuffer, int numBytes) : - Assignment(dataBuffer, numBytes), - _shouldStop(false) + ThreadedAssignment(dataBuffer, numBytes) { } - -void Agent::stop() { - _shouldStop = true; -} - -static size_t writeScriptDataToString(void *contents, size_t size, size_t nmemb, void *userdata) { - size_t realSize = size * nmemb; - - QString* scriptContents = (QString*) userdata; - - // append this chunk to the scriptContents - scriptContents->append(QByteArray((char*) contents, realSize)); - - // return the amount of data read - return realSize; -} - QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3) { QScriptValue obj = engine->newObject(); obj.setProperty("x", vec3.x); @@ -53,6 +39,16 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) { vec3.z = object.property("z").toVariant().toFloat(); } +void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { + if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION) { + _voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, + (unsigned char*) dataByteArray.data(), + dataByteArray.size()); + } else { + NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + } +} + void Agent::run() { NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AGENT); @@ -60,149 +56,93 @@ void Agent::run() { const char AGENT_NODE_TYPES_OF_INTEREST[1] = { NODE_TYPE_VOXEL_SERVER }; nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST)); - - nodeList->getNodeSocket()->setBlocking(false); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(), uuidStringWithoutCurlyBraces(_uuid)); - // setup curl for script download - CURLcode curlResult; - - CURL* curlHandle = curl_easy_init(); - - // tell curl which file to grab - curl_easy_setopt(curlHandle, CURLOPT_URL, scriptURLString.toStdString().c_str()); - - // send the data to the WriteMemoryCallback function - curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, writeScriptDataToString); - - QString scriptContents; - - // pass the scriptContents QString to append data to - curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *)&scriptContents); - - // send a user agent since some servers will require it - curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - - // make sure CURL fails on a 400 code - curl_easy_setopt(curlHandle, CURLOPT_FAILONERROR, true); + QNetworkAccessManager *networkManager = new QNetworkAccessManager(this); + QNetworkReply *reply = networkManager->get(QNetworkRequest(QUrl(scriptURLString))); qDebug() << "Downloading script at" << scriptURLString << "\n"; - // blocking get for JS file - curlResult = curl_easy_perform(curlHandle); - - if (curlResult == CURLE_OK) { - // cleanup curl - curl_easy_cleanup(curlHandle); - curl_global_cleanup(); + QEventLoop loop; + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + + loop.exec(); + + QString scriptContents(reply->readAll()); + QScriptEngine engine; + + // register meta-type for glm::vec3 conversions + qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue); + + QScriptValue agentValue = engine.newQObject(this); + engine.globalObject().setProperty("Agent", agentValue); + + QScriptValue voxelScripterValue = engine.newQObject(&_voxelScriptingInterface); + engine.globalObject().setProperty("Voxels", voxelScripterValue); + + QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); + engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); + + const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; + + // let the VoxelPacketSender know how frequently we plan to call it + _voxelScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); + + qDebug() << "Downloaded script:" << scriptContents << "\n"; + QScriptValue result = engine.evaluate(scriptContents); + qDebug() << "Evaluated script.\n"; + + if (engine.hasUncaughtException()) { + int line = engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + } + + timeval startTime; + gettimeofday(&startTime, NULL); + + int thisFrame = 0; + + 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); + + while (!_isFinished) { - QScriptEngine engine; + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); + if (usecToSleep > 0) { + usleep(usecToSleep); + } - // register meta-type for glm::vec3 conversions - qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue); + QCoreApplication::processEvents(); - QScriptValue agentValue = engine.newQObject(this); - engine.globalObject().setProperty("Agent", agentValue); - - VoxelScriptingInterface voxelScripter; - QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); - - const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; - - // let the VoxelPacketSender know how frequently we plan to call it - voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); - - // hook in a constructor for audio injectorss - AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - QScriptValue audioInjectorValue = engine.newQObject(&scriptedAudioInjector); - engine.globalObject().setProperty("AudioInjector", audioInjectorValue); - - qDebug() << "Downloaded script:" << scriptContents << "\n"; - QScriptValue result = engine.evaluate(scriptContents); - qDebug() << "Evaluated script.\n"; + if (_voxelScriptingInterface.getVoxelPacketSender()->voxelServersExist()) { + // allow the scripter's call back to setup visual data + emit willSendVisualDataCallback(); + + // release the queue of edit voxel messages. + _voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); + + // since we're in non-threaded mode, call process so that the packets are sent + _voxelScriptingInterface.getVoxelPacketSender()->process(); + } if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; } - timeval startTime; - gettimeofday(&startTime, NULL); - timeval lastDomainServerCheckIn = {}; - - sockaddr_in senderAddress; - unsigned char receivedData[MAX_PACKET_SIZE]; - ssize_t receivedBytes; - - int thisFrame = 0; - - NodeList::getInstance()->startSilentNodeRemovalThread(); - - while (!_shouldStop) { - - // if we're not hearing from the domain-server we should stop running - if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { - break; - } - - // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed - if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { - gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(); - } - - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); - if (usecToSleep > 0) { - usleep(usecToSleep); - } - - if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) { - timeval thisSend = {}; - gettimeofday(&thisSend, NULL); - // allow the scripter's call back to setup visual data - emit willSendVisualDataCallback(); - - // release the queue of edit voxel messages. - voxelScripter.getVoxelPacketSender()->releaseQueuedMessages(); - - // since we're in non-threaded mode, call process so that the packets are sent - voxelScripter.getVoxelPacketSender()->process(); - } - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; - } - - while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes) - && packetVersionMatch(receivedData)) { - if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { - voxelScripter.getJurisdictionListener()->queueReceivedPacket((sockaddr&) senderAddress, - receivedData, - receivedBytes); - } else { - NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); - } - } - } - - NodeList::getInstance()->stopSilentNodeRemovalThread(); - - } else { - // error in curl_easy_perform - qDebug() << "curl_easy_perform for JS failed:" << curl_easy_strerror(curlResult) << "\n"; - - // cleanup curl - curl_easy_cleanup(curlHandle); - curl_global_cleanup(); } } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 552910e04d..5f0865e420 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -15,25 +15,24 @@ #include #include -#include -#include +#include -class Agent : public Assignment { +#include "voxels/VoxelScriptingInterface.h" + +class Agent : public ThreadedAssignment { Q_OBJECT public: Agent(const unsigned char* dataBuffer, int numBytes); - void run(); public slots: - void stop(); + void run(); + + void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); signals: void willSendAudioDataCallback(); void willSendVisualDataCallback(); private: - static QScriptValue AudioInjectorConstructor(QScriptContext *context, QScriptEngine *engine); - - bool volatile _shouldStop; - std::vector _audioInjectors; + VoxelScriptingInterface _voxelScriptingInterface; }; #endif /* defined(__hifi__Agent__) */ diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp new file mode 100644 index 0000000000..fff24a51a3 --- /dev/null +++ b/assignment-client/src/AssignmentClient.cpp @@ -0,0 +1,140 @@ +// +// AssignmentClient.cpp +// hifi +// +// Created by Stephen Birarda on 11/25/2013. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include +#include + +#include +#include +#include +#include +#include + +#include "AssignmentFactory.h" + +#include "AssignmentClient.h" + +const char ASSIGNMENT_CLIENT_TARGET_NAME[] = "assignment-client"; +const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000; + +AssignmentClient::AssignmentClient(int &argc, char **argv, + Assignment::Type requestAssignmentType, + const HifiSockAddr& customAssignmentServerSocket, + const char* requestAssignmentPool) : + QCoreApplication(argc, argv), + _requestAssignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool), + _currentAssignment(NULL) +{ + // register meta type is required for queued invoke method on Assignment subclasses + qRegisterMetaType("HifiSockAddr"); + + // set the logging target to the the CHILD_TARGET_NAME + Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); + + // create a NodeList as an unassigned client + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); + + // set the custom assignment socket if we have it + if (!customAssignmentServerSocket.isNull()) { + nodeList->setAssignmentServerSocket(customAssignmentServerSocket); + } + + // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required + qDebug() << "Waiting for assignment -" << _requestAssignment << "\n"; + + QTimer* timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); + timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); + + // connect our readPendingDatagrams method to the readyRead() signal of the socket + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); +} + +void AssignmentClient::sendAssignmentRequest() { + if (!_currentAssignment) { + NodeList::getInstance()->sendAssignment(_requestAssignment); + } +} + +void AssignmentClient::readPendingDatagrams() { + NodeList* nodeList = NodeList::getInstance(); + + static unsigned char packetData[1500]; + static qint64 receivedBytes = 0; + static HifiSockAddr senderSockAddr; + + while (nodeList->getNodeSocket().hasPendingDatagrams()) { + + if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, + senderSockAddr.getAddressPointer(), + senderSockAddr.getPortPointer())) + && packetVersionMatch(packetData)) { + + if (_currentAssignment) { + // have the threaded current assignment handle this datagram + QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection, + Q_ARG(const QByteArray&, QByteArray((char*) packetData, receivedBytes)), + Q_ARG(const HifiSockAddr&, senderSockAddr)); + } else if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { + + if (_currentAssignment) { + qDebug() << "Dropping received assignment since we are currently running one.\n"; + } else { + // construct the deployed assignment from the packet data + _currentAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes); + + qDebug() << "Received an assignment -" << *_currentAssignment << "\n"; + + // switch our nodelist domain IP and port to whoever sent us the assignment + if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { + nodeList->setDomainSockAddr(senderSockAddr); + nodeList->setOwnerUUID(_currentAssignment->getUUID()); + + qDebug("Destination IP for assignment is %s\n", + nodeList->getDomainIP().toString().toStdString().c_str()); + + // start the deployed assignment + QThread* workerThread = new QThread(this); + + connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); + + connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); + connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); + connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); + connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); + + _currentAssignment->moveToThread(workerThread); + + // Starts an event loop, and emits workerThread->started() + workerThread->start(); + } else { + qDebug("Received a bad destination socket for assignment.\n"); + } + } + } else { + // have the NodeList attempt to handle it + nodeList->processNodeData(senderSockAddr, packetData, receivedBytes); + } + } + } +} + +void AssignmentClient::assignmentCompleted() { + // reset the logging target to the the CHILD_TARGET_NAME + Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); + + qDebug("Assignment finished or never started - waiting for new assignment\n"); + + _currentAssignment = NULL; + + NodeList* nodeList = NodeList::getInstance(); + + // reset our NodeList by switching back to unassigned and clearing the list + nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); + nodeList->reset(); +} diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h new file mode 100644 index 0000000000..48311b0275 --- /dev/null +++ b/assignment-client/src/AssignmentClient.h @@ -0,0 +1,32 @@ +// +// AssignmentClient.h +// hifi +// +// Created by Stephen Birarda on 11/25/2013. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AssignmentClient__ +#define __hifi__AssignmentClient__ + +#include + +#include "ThreadedAssignment.h" + +class AssignmentClient : public QCoreApplication { + Q_OBJECT +public: + AssignmentClient(int &argc, char **argv, + Assignment::Type requestAssignmentType = Assignment::AllTypes, + const HifiSockAddr& customAssignmentServerSocket = HifiSockAddr(), + const char* requestAssignmentPool = NULL); +private slots: + void sendAssignmentRequest(); + void readPendingDatagrams(); + void assignmentCompleted(); +private: + Assignment _requestAssignment; + ThreadedAssignment* _currentAssignment; +}; + +#endif /* defined(__hifi__AssignmentClient__) */ diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index a3aa657943..aa173f920f 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -15,7 +15,7 @@ #include "AssignmentFactory.h" -Assignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) { +ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) { int headerBytes = numBytesForPacketHeader(dataBuffer); Assignment::Type assignmentType = Assignment::AllTypes; @@ -31,6 +31,6 @@ Assignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, case Assignment::VoxelServerType: return new VoxelServer(dataBuffer, numBytes); default: - return new Assignment(dataBuffer, numBytes); + return NULL; } } \ No newline at end of file diff --git a/assignment-client/src/AssignmentFactory.h b/assignment-client/src/AssignmentFactory.h index 8adadb07ed..4605d961ec 100644 --- a/assignment-client/src/AssignmentFactory.h +++ b/assignment-client/src/AssignmentFactory.h @@ -9,11 +9,11 @@ #ifndef __hifi__AssignmentFactory__ #define __hifi__AssignmentFactory__ -#include "Assignment.h" +#include class AssignmentFactory { public: - static Assignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes); + static ThreadedAssignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes); }; #endif /* defined(__hifi__AssignmentFactory__) */ diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index c8897fe2b1..08b5668389 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -32,6 +32,9 @@ #include #include +#include +#include + #include #include #include @@ -51,7 +54,7 @@ const short JITTER_BUFFER_MSECS = 12; const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0); -const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); +const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000 * 1000); const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); const int MIN_SAMPLE_VALUE = std::numeric_limits::min(); @@ -64,7 +67,9 @@ void attachNewBufferToNode(Node *newNode) { } } -AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) { +AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) : + ThreadedAssignment(dataBuffer, numBytes) +{ } @@ -217,78 +222,77 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { } } + +void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { + // pull any new audio data from nodes off of the network stack + if (dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO + || dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO + || dataByteArray[0] == PACKET_TYPE_INJECT_AUDIO) { + QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()), + NUM_BYTES_RFC4122_UUID)); + + NodeList* nodeList = NodeList::getInstance(); + + Node* matchingNode = nodeList->nodeWithUUID(nodeUUID); + + if (matchingNode) { + nodeList->updateNodeWithData(matchingNode, senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + + if (!matchingNode->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 + matchingNode->activatePublicSocket(); + } + } + } else { + // let processNodeData handle it. + NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + } +} + void AudioMixer::run() { + + NodeList* nodeList = NodeList::getInstance(); + // change the logging target name while this is running Logging::setTargetName(AUDIO_MIXER_LOGGING_TARGET_NAME); - NodeList *nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER); const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR }; nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST)); - ssize_t receivedBytes = 0; - nodeList->linkedDataCreateCallback = attachNewBufferToNode; - nodeList->startSilentNodeRemovalThread(); + QTimer* domainServerTimer = new QTimer(this); + connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); + domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - unsigned char packetData[MAX_PACKET_SIZE] = {}; + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - sockaddr* nodeAddress = new sockaddr; - - // make sure our node socket is non-blocking - nodeList->getNodeSocket()->setBlocking(false); + QTimer* pingNodesTimer = new QTimer(this); + connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); + pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); int nextFrame = 0; timeval startTime; + gettimeofday(&startTime, NULL); + int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MIXED_AUDIO); unsigned char clientPacket[BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader]; populateTypeAndVersion(clientPacket, PACKET_TYPE_MIXED_AUDIO); - gettimeofday(&startTime, NULL); - - timeval lastDomainServerCheckIn = {}; - - timeval beginSendTime, endSendTime; - float sumFrameTimePercentages = 0.0f; - int numStatCollections = 0; - - // if we'll be sending stats, call the Logstash::socket() method to make it load the logstash IP outside the loop - if (Logging::shouldSendStats()) { - Logging::socket(); - } - - while (true) { - if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + while (!_isFinished) { + + QCoreApplication::processEvents(); + + if (_isFinished) { break; } - - if (Logging::shouldSendStats()) { - gettimeofday(&beginSendTime, NULL); - } - - // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed - if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { - gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(); - - if (Logging::shouldSendStats() && numStatCollections > 0) { - // if we should be sending stats to Logstash send the appropriate average now - const char MIXER_LOGSTASH_METRIC_NAME[] = "audio-mixer-frame-time-usage"; - - float averageFrameTimePercentage = sumFrameTimePercentages / numStatCollections; - Logging::stashValue(STAT_TYPE_TIMER, MIXER_LOGSTASH_METRIC_NAME, averageFrameTimePercentage); - - sumFrameTimePercentages = 0.0f; - numStatCollections = 0; - } - } - - // get the NodeList to ping any inactive nodes, for hole punching - nodeList->possiblyPingInactiveNodes(); - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData()) { ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES); @@ -301,7 +305,9 @@ void AudioMixer::run() { prepareMixForListeningNode(&(*node)); memcpy(clientPacket + numBytesPacketHeader, _clientSamples, sizeof(_clientSamples)); - nodeList->getNodeSocket()->send(node->getActiveSocket(), clientPacket, sizeof(clientPacket)); + nodeList->getNodeSocket().writeDatagram((char*) clientPacket, sizeof(clientPacket), + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); } } @@ -312,47 +318,6 @@ void AudioMixer::run() { } } - // pull any new audio data from nodes off of the network stack - while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) && - packetVersionMatch(packetData)) { - if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO - || packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO - || packetData[0] == PACKET_TYPE_INJECT_AUDIO) { - - QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), - NUM_BYTES_RFC4122_UUID)); - - Node* matchingNode = nodeList->nodeWithUUID(nodeUUID); - - if (matchingNode) { - nodeList->updateNodeWithData(matchingNode, nodeAddress, packetData, receivedBytes); - - if (!matchingNode->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 - matchingNode->activatePublicSocket(); - } - } - } else { - // let processNodeData handle it. - nodeList->processNodeData(nodeAddress, packetData, receivedBytes); - } - } - - if (Logging::shouldSendStats()) { - // send a packet to our logstash instance - - // calculate the percentage value for time elapsed for this send (of the max allowable time) - gettimeofday(&endSendTime, NULL); - - float percentageOfMaxElapsed = ((float) (usecTimestamp(&endSendTime) - usecTimestamp(&beginSendTime)) - / BUFFER_SEND_INTERVAL_USECS) * 100.0f; - - sumFrameTimePercentages += percentageOfMaxElapsed; - - numStatCollections++; - } - int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { @@ -360,5 +325,6 @@ void AudioMixer::run() { } else { qDebug("Took too much time, not sleeping!\n"); } + } } \ No newline at end of file diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 4cf0398b9b..2c07e8747a 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -9,19 +9,23 @@ #ifndef __hifi__AudioMixer__ #define __hifi__AudioMixer__ -#include #include +#include + class PositionalAudioRingBuffer; class AvatarAudioRingBuffer; /// Handles assignments of type AudioMixer - mixing streams of audio and re-distributing to various clients. -class AudioMixer : public Assignment { +class AudioMixer : public ThreadedAssignment { + Q_OBJECT public: AudioMixer(const unsigned char* dataBuffer, int numBytes); - - /// runs the audio mixer +public slots: + /// threaded run of assignment void run(); + + void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); private: /// adds one buffer to the mix for a listening node void addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd, diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b13c92e0ed..4fd46e37f6 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -10,6 +10,9 @@ // The avatar mixer receives head, hand and positional data from all connected // nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms. +#include +#include + #include #include #include @@ -22,6 +25,14 @@ const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer"; +const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000; + +AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : + ThreadedAssignment(dataBuffer, numBytes) +{ + +} + unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) { QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122(); memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size()); @@ -46,7 +57,7 @@ void attachAvatarDataToNode(Node* newNode) { // 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to // determine which avatars are included in the packet stream // 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful). -void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, sockaddr* receiverAddress) { +void broadcastAvatarData() { static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE]; static unsigned char avatarDataBuffer[MAX_PACKET_SIZE]; unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0]; @@ -55,38 +66,91 @@ void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, sockaddr int packetLength = currentBufferPosition - broadcastPacket; int packetsSent = 0; - // send back a packet with other active node data to this node + NodeList* nodeList = NodeList::getInstance(); + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getLinkedData() && node->getUUID() != receiverUUID) { - unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node); - int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer; - - if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) { - memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); - packetLength += avatarDataLength; - currentBufferPosition += avatarDataLength; - } else { - packetsSent++; - //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); - nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket); - - // reset the packet - currentBufferPosition = broadcastPacket + numHeaderBytes; - packetLength = currentBufferPosition - broadcastPacket; - - // copy the avatar that didn't fit into the next packet - memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); - packetLength += avatarDataLength; - currentBufferPosition += avatarDataLength; + if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT && node->getActiveSocket()) { + // this is an AGENT we have received head data from + // send back a packet with other active node data to this node + for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) { + if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) { + unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node); + int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer; + + if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) { + memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); + packetLength += avatarDataLength; + currentBufferPosition += avatarDataLength; + } else { + packetsSent++; + //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); + nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + + // reset the packet + currentBufferPosition = broadcastPacket + numHeaderBytes; + packetLength = currentBufferPosition - broadcastPacket; + + // copy the avatar that didn't fit into the next packet + memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); + packetLength += avatarDataLength; + currentBufferPosition += avatarDataLength; + } + } } + + packetsSent++; + //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); } } - packetsSent++; - //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); - nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket); + + } -AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) { +void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { + + NodeList* nodeList = NodeList::getInstance(); + + switch (dataByteArray[0]) { + case PACKET_TYPE_HEAD_DATA: { + QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()), + NUM_BYTES_RFC4122_UUID)); + + // add or update the node in our list + Node* avatarNode = nodeList->nodeWithUUID(nodeUUID); + + if (avatarNode) { + // parse positional data from an node + nodeList->updateNodeWithData(avatarNode, senderSockAddr, + (unsigned char*) dataByteArray.data(), dataByteArray.size()); + } else { + break; + } + } + case PACKET_TYPE_KILL_NODE: + case PACKET_TYPE_AVATAR_URLS: + case PACKET_TYPE_AVATAR_FACE_VIDEO: { + QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()), + NUM_BYTES_RFC4122_UUID)); + // let everyone else know about the update + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getActiveSocket() && node->getUUID() != nodeUUID) { + nodeList->getNodeSocket().writeDatagram(dataByteArray, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + } + } + // let node kills fall through to default behavior + } + default: + // hand this off to the NodeList + nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); + break; + } } @@ -101,71 +165,39 @@ void AvatarMixer::run() { nodeList->linkedDataCreateCallback = attachAvatarDataToNode; - nodeList->startSilentNodeRemovalThread(); + QTimer* domainServerTimer = new QTimer(this); + connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); + domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - sockaddr nodeAddress = {}; - ssize_t receivedBytes = 0; + QTimer* pingNodesTimer = new QTimer(this); + connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); + pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - unsigned char packetData[MAX_PACKET_SIZE]; + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - QUuid nodeUUID; - Node* avatarNode = NULL; + int nextFrame = 0; + timeval startTime; - timeval lastDomainServerCheckIn = {}; + gettimeofday(&startTime, NULL); - while (true) { + while (!_isFinished) { - if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + QCoreApplication::processEvents(); + + if (_isFinished) { break; } - // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed - if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { - gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(); - } + broadcastAvatarData(); - nodeList->possiblyPingInactiveNodes(); + int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow(); - if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) && - packetVersionMatch(packetData)) { - switch (packetData[0]) { - case PACKET_TYPE_HEAD_DATA: - nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), - NUM_BYTES_RFC4122_UUID)); - - // add or update the node in our list - avatarNode = nodeList->nodeWithUUID(nodeUUID); - - if (avatarNode) { - // parse positional data from an node - nodeList->updateNodeWithData(avatarNode, &nodeAddress, packetData, receivedBytes); - } else { - break; - } - case PACKET_TYPE_INJECT_AUDIO: - broadcastAvatarData(nodeList, nodeUUID, &nodeAddress); - break; - case PACKET_TYPE_KILL_NODE: - case PACKET_TYPE_AVATAR_URLS: - case PACKET_TYPE_AVATAR_FACE_VIDEO: - nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), - NUM_BYTES_RFC4122_UUID)); - // let everyone else know about the update - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getActiveSocket() && node->getUUID() != nodeUUID) { - nodeList->getNodeSocket()->send(node->getActiveSocket(), packetData, receivedBytes); - } - } - // let node kills fall through to default behavior - - default: - // hand this off to the NodeList - nodeList->processNodeData(&nodeAddress, packetData, receivedBytes); - break; - } + if (usecToSleep > 0) { + usleep(usecToSleep); + } else { + qDebug() << "Took too much time, not sleeping!\n"; } } - - nodeList->stopSilentNodeRemovalThread(); } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 41a584a296..c2158fad1c 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -9,15 +9,18 @@ #ifndef __hifi__AvatarMixer__ #define __hifi__AvatarMixer__ -#include +#include /// Handles assignments of type AvatarMixer - distribution of avatar data to various clients -class AvatarMixer : public Assignment { +class AvatarMixer : public ThreadedAssignment { public: AvatarMixer(const unsigned char* dataBuffer, int numBytes); +public slots: /// runs the avatar mixer void run(); + + void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); }; #endif /* defined(__hifi__AvatarMixer__) */ diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 6f8cd83784..28516b37b8 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -20,16 +20,14 @@ #include "Agent.h" #include "Assignment.h" -#include "AssignmentFactory.h" +#include "AssignmentClient.h" #include "audio/AudioMixer.h" #include "avatars/AvatarMixer.h" -const long long ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000; const char PARENT_TARGET_NAME[] = "assignment-client-monitor"; -const char CHILD_TARGET_NAME[] = "assignment-client"; pid_t* childForks = NULL; -sockaddr_in customAssignmentSocket = {}; +HifiSockAddr customAssignmentSocket; int numForks = 0; Assignment::Type overiddenAssignmentType = Assignment::AllTypes; const char* assignmentPool = NULL; @@ -37,84 +35,9 @@ const char* assignmentPool = NULL; int argc = 0; char** argv = NULL; -void childClient() { - QCoreApplication application(::argc, ::argv); - - // set the logging target to the the CHILD_TARGET_NAME - Logging::setTargetName(CHILD_TARGET_NAME); - - // create a NodeList as an unassigned client - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); - - // set the custom assignment socket if we have it - if (customAssignmentSocket.sin_addr.s_addr != 0) { - nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket); - } - - // change the timeout on the nodelist socket to be as often as we want to re-request - nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS); - - timeval lastRequest = {}; - - unsigned char packetData[MAX_PACKET_SIZE]; - ssize_t receivedBytes = 0; - - sockaddr_in senderSocket = {}; - - // create a request assignment, accept assignments defined by the overidden type - Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType, ::assignmentPool); - - qDebug() << "Waiting for assignment -" << requestAssignment << "\n"; - - while (true) { - if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) { - gettimeofday(&lastRequest, NULL); - - // if we're here we have no assignment, so send a request - nodeList->sendAssignment(requestAssignment); - } - - if (nodeList->getNodeSocket()->receive((sockaddr*) &senderSocket, packetData, &receivedBytes) && - (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) - && packetVersionMatch(packetData)) { - - // construct the deployed assignment from the packet data - Assignment* deployedAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes); - - qDebug() << "Received an assignment -" << *deployedAssignment << "\n"; - - // switch our nodelist domain IP and port to whoever sent us the assignment - if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { - - nodeList->setDomainIP(QHostAddress((sockaddr*) &senderSocket)); - nodeList->setDomainPort(ntohs(senderSocket.sin_port)); - - nodeList->setOwnerUUID(deployedAssignment->getUUID()); - - qDebug("Destination IP for assignment is %s\n", nodeList->getDomainIP().toString().toStdString().c_str()); - - // run the deployed assignment - deployedAssignment->run(); - } else { - qDebug("Received a bad destination socket for assignment.\n"); - } - - qDebug("Assignment finished or never started - waiting for new assignment\n"); - - // delete the deployedAssignment - delete deployedAssignment; - - // reset our NodeList by switching back to unassigned and clearing the list - nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); - nodeList->reset(); - - // set the NodeList socket back to blocking - nodeList->getNodeSocket()->setBlocking(true); - - // reset the logging target to the the CHILD_TARGET_NAME - Logging::setTargetName(CHILD_TARGET_NAME); - } - } +int childClient() { + AssignmentClient client(::argc, ::argv, ::overiddenAssignmentType, customAssignmentSocket, ::assignmentPool); + return client.exec(); } void sigchldHandler(int sig) { @@ -203,10 +126,10 @@ int main(int argc, char* argv[]) { // set the custom hostname or default if it wasn't passed if (!customAssignmentServerHostname) { - customAssignmentServerHostname = LOCAL_ASSIGNMENT_SERVER_HOSTNAME; + customAssignmentServerHostname = DEFAULT_ASSIGNMENT_SERVER_HOSTNAME; } - ::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServerHostname, assignmentServerPort); + ::customAssignmentSocket = HifiSockAddr(customAssignmentServerHostname, assignmentServerPort); } const char ASSIGNMENT_TYPE_OVVERIDE_OPTION[] = "-t"; @@ -247,7 +170,7 @@ int main(int argc, char* argv[]) { } if (processID == 0 || ::numForks == 0) { - childClient(); + return childClient(); } else { parentMonitor(); } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 792942f846..b10006e6cc 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -30,14 +30,11 @@ void DomainServer::setDomainServerInstance(DomainServer* domainServer) { domainServerInstance = domainServer; } -QJsonObject jsonForSocket(sockaddr* socket) { +QJsonObject jsonForSocket(const HifiSockAddr& socket) { QJsonObject socketJSON; - if (socket->sa_family == AF_INET) { - sockaddr_in* socketIPv4 = (sockaddr_in*) socket; - socketJSON["ip"] = QString(inet_ntoa(socketIPv4->sin_addr)); - socketJSON["port"] = (int) ntohs(socketIPv4->sin_port); - } + socketJSON["ip"] = socket.getAddress().toString(); + socketJSON["port"] = ntohs(socket.getPort()); return socketJSON; } @@ -288,8 +285,8 @@ unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosi memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size()); currentPosition += rfcUUID.size(); - currentPosition += packSocket(currentPosition, nodeToAdd->getPublicSocket()); - currentPosition += packSocket(currentPosition, nodeToAdd->getLocalSocket()); + currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getPublicSocket()); + currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getLocalSocket()); // return the new unsigned char * for broadcast packet return currentPosition; @@ -506,15 +503,15 @@ void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) { _assignmentQueueMutex.unlock(); } -bool DomainServer::checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, - sockaddr* nodeLocalSocket, +bool DomainServer::checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket, + const HifiSockAddr& nodeLocalSocket, const QUuid& checkInUUID) { NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() - && socketMatch(node->getPublicSocket(), nodePublicSocket) - && socketMatch(node->getLocalSocket(), nodeLocalSocket) + && nodePublicSocket == node->getPublicSocket() + && nodeLocalSocket == node->getLocalSocket() && node->getUUID() == checkInUUID) { // this is a matching existing node if the public socket, local socket, and UUID match return true; @@ -588,9 +585,9 @@ int DomainServer::run() { unsigned char* currentBufferPos; unsigned char* startPointer; - sockaddr_in senderAddress, nodePublicAddress, nodeLocalAddress; - nodePublicAddress.sin_family = AF_INET; - nodeLocalAddress.sin_family = AF_INET; + QHostAddress senderAddress; + quint16 senderPort; + HifiSockAddr nodePublicAddress, nodeLocalAddress; nodeList->startSilentNodeRemovalThread(); @@ -614,7 +611,8 @@ int DomainServer::run() { gettimeofday(&startTime, NULL); while (true) { - while (nodeList->getNodeSocket()->receive((sockaddr *)&senderAddress, packetData, &receivedBytes) && + while (nodeList->getNodeSocket().hasPendingDatagrams() + && nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, &senderAddress, &senderPort) && packetVersionMatch(packetData)) { if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) { // this is an RFD or domain list request packet, and there is a version match @@ -627,23 +625,23 @@ int DomainServer::run() { QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID)); packetIndex += NUM_BYTES_RFC4122_UUID; - int numBytesPrivateSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodePublicAddress); + int numBytesPrivateSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodePublicAddress); packetIndex += numBytesPrivateSocket; - if (nodePublicAddress.sin_addr.s_addr == 0) { + if (nodePublicAddress.getAddress().isNull()) { // this node wants to use us its STUN server // so set the node public address to whatever we perceive the public address to be - nodePublicAddress = senderAddress; - // if the sender is on our box then leave its public address to 0 so that // other users attempt to reach it on the same address they have for the domain-server - if (senderAddress.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { - nodePublicAddress.sin_addr.s_addr = 0; + if (senderAddress.isLoopback()) { + nodePublicAddress.setAddress(QHostAddress()); + } else { + nodePublicAddress.setAddress(senderAddress); } } - int numBytesPublicSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodeLocalAddress); + int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress); packetIndex += numBytesPublicSocket; const char STATICALLY_ASSIGNED_NODES[3] = { @@ -656,14 +654,14 @@ int DomainServer::run() { if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL || ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType)) - || checkInWithUUIDMatchesExistingNode((sockaddr*) &nodePublicAddress, - (sockaddr*) &nodeLocalAddress, + || checkInWithUUIDMatchesExistingNode(nodePublicAddress, + nodeLocalAddress, nodeUUID))) { Node* checkInNode = nodeList->addOrUpdateNode(nodeUUID, nodeType, - (sockaddr*) &nodePublicAddress, - (sockaddr*) &nodeLocalAddress); + nodePublicAddress, + nodeLocalAddress); if (matchingStaticAssignment) { // this was a newly added node with a matching static assignment @@ -691,7 +689,7 @@ int DomainServer::run() { if (numInterestTypes > 0) { // if the node has sent no types of interest, assume they want nothing but their own ID back for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) && + if (node->getUUID() != nodeUUID && memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) { // don't send avatar nodes to other avatars, that will come from avatar mixer @@ -708,9 +706,9 @@ int DomainServer::run() { checkInNode->setLastHeardMicrostamp(timeNow); // send the constructed list back to this node - nodeList->getNodeSocket()->send((sockaddr*)&senderAddress, - broadcastPacket, - (currentBufferPos - startPointer) + numHeaderBytes); + nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, + (currentBufferPos - startPointer) + numHeaderBytes, + senderAddress, senderPort); } } else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) { @@ -732,9 +730,8 @@ int DomainServer::run() { int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT); int numAssignmentBytes = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes); - nodeList->getNodeSocket()->send((sockaddr*) &senderAddress, - broadcastPacket, - numHeaderBytes + numAssignmentBytes); + nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, numHeaderBytes + numAssignmentBytes, + senderAddress, senderPort); if (assignmentToDeploy->getNumberOfInstances() == 0) { // there are no more instances of this script to send out, delete it @@ -743,43 +740,6 @@ int DomainServer::run() { } } - } else if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { - // this is a create assignment likely recieved from a server needed more clients to help with load - - // unpack it - Assignment* createAssignment = new Assignment(packetData, receivedBytes); - - qDebug() << "Received a create assignment -" << *createAssignment << "\n"; - - // make sure we have a matching node with the UUID packed with the assignment - // if the node has sent no types of interest, assume they want nothing but their own ID back - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getLinkedData() - && socketMatch((sockaddr*) &senderAddress, node->getPublicSocket()) - && ((Assignment*) node->getLinkedData())->getUUID() == createAssignment->getUUID()) { - - // give the create assignment a new UUID - createAssignment->resetUUID(); - - // add the assignment at the back of the queue - _assignmentQueueMutex.lock(); - _assignmentQueue.push_back(createAssignment); - _assignmentQueueMutex.unlock(); - - // find the first available spot in the static assignments and put this assignment there - for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) { - if (_staticAssignments[i].getUUID().isNull()) { - _staticAssignments[i] = *createAssignment; - - // we've stuck the assignment in, break out - break; - } - } - - // we found the matching node that asked for create assignment, break out - break; - } - } } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 45730847ac..755f73485c 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -45,7 +45,9 @@ private: Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType); Assignment* deployableAssignmentForRequest(Assignment& requestAssignment); void removeAssignmentFromQueue(Assignment* removableAssignment); - bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const QUuid& checkInUUI); + bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket, + const HifiSockAddr& nodeLocalSocket, + const QUuid& checkInUUI); void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime); void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 13b5726c42..833f811bf8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -19,6 +19,7 @@ #include #endif +#include #include #include @@ -47,8 +48,6 @@ #include #include -#include -#include #include #include #include @@ -126,6 +125,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _mouseVoxelScale(1.0f / 1024.0f), _mouseVoxelScaleInitialized(false), _justEditedVoxel(false), + _isHighlightVoxel(false), _nudgeStarted(false), _lookingAlongX(false), _lookingAwayFromOrigin(true), @@ -138,6 +138,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : #endif _stopNetworkReceiveThread(false), _voxelProcessor(), + _voxelHideShowThread(&_voxels), _voxelEditSender(this), _packetCount(0), _packetsPerSecond(0), @@ -170,9 +171,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // network receive thread and voxel parsing thread are both controlled by the --nonblocking command line _enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); - if (!_enableNetworkThread) { - NodeList::getInstance()->getNodeSocket()->setBlocking(false); - } // setup QSettings #ifdef Q_OS_MAC @@ -316,6 +314,7 @@ void Application::initializeGL() { // create thread for parsing of voxel data independent of the main network and rendering threads _voxelProcessor.initialize(_enableProcessVoxelsThread); _voxelEditSender.initialize(_enableProcessVoxelsThread); + _voxelHideShowThread.initialize(_enableProcessVoxelsThread); if (_enableProcessVoxelsThread) { qDebug("Voxel parsing thread created.\n"); } @@ -1385,6 +1384,7 @@ void Application::terminate() { } _voxelProcessor.terminate(); + _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); } @@ -1445,8 +1445,9 @@ void Application::checkBandwidthMeterClick() { // ... to be called upon button release if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth) && - glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY))) <= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH && - _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) { + glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY))) + <= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH + && _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) { // The bandwidth meter is visible, the click didn't get dragged too far and // we actually hit the bandwidth meter @@ -1756,8 +1757,6 @@ void Application::init() { _voxelShader.init(); _pointShader.init(); - _handControl.setScreenDimensions(_glWidget->width(), _glWidget->height()); - _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; QCursor::setPos(_headMouseX, _headMouseY); @@ -1982,6 +1981,20 @@ void Application::renderFollowIndicator() { } } +void Application::renderHighlightVoxel(VoxelDetail voxel) { + glDisable(GL_LIGHTING); + glPushMatrix(); + glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); + const float EDGE_EXPAND = 1.02f; + glColor3ub(voxel.red + 128, voxel.green + 128, voxel.blue + 128); + glTranslatef(voxel.x + voxel.s * 0.5f, + voxel.y + voxel.s * 0.5f, + voxel.z + voxel.s * 0.5f); + glLineWidth(2.0f); + glutWireCube(voxel.s * EDGE_EXPAND); + glPopMatrix(); +} + void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); @@ -2227,11 +2240,6 @@ void Application::updateHandAndTouch(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()"); - // walking triggers the handControl to stop - if (_myAvatar.getMode() == AVATAR_MODE_WALKING) { - _handControl.stop(); - } - // Update from Touch if (_isTouchPressed) { float TOUCH_YAW_SCALE = -0.25f; @@ -2281,6 +2289,7 @@ void Application::updateThreads(float deltaTime) { // parse voxel packets if (!_enableProcessVoxelsThread) { _voxelProcessor.threadRoutine(); + _voxelHideShowThread.threadRoutine(); _voxelEditSender.threadRoutine(); } } @@ -2690,7 +2699,6 @@ void Application::queryVoxels() { qDebug("perServerPPS: %d perUnknownServer: %d\n", perServerPPS, perUnknownServer); } - UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) { @@ -2776,7 +2784,8 @@ void Application::queryVoxels() { int packetLength = endOfVoxelQueryPacket - voxelQueryPacket; - nodeSocket->send(node->getActiveSocket(), voxelQueryPacket, packetLength); + nodeList->getNodeSocket().writeDatagram((char*) voxelQueryPacket, packetLength, + node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort()); // Feed number of bytes to corresponding channel of the bandwidth meter _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(packetLength); @@ -3012,10 +3021,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } } - // restore default, white specular glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR); + // Render the highlighted voxel + if (_isHighlightVoxel) { + renderHighlightVoxel(_highlightVoxel); + } + // indicate what we'll be adding/removing in mouse mode, if anything if (_mouseVoxel.s != 0 && whichCamera.getMode() != CAMERA_MODE_MIRROR) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), @@ -3039,7 +3052,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } else { renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); } - + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) { // use a contrasting color so that we can see what we're doing glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128); @@ -3924,62 +3937,6 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { } } -void Application::injectVoxelAddedSoundEffect() { - AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025); - - if (voxelInjector) { - voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z)); - //voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw()); - voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value - - /* for (int i = 0; i - < 22050; i++) { - if (i % 4 == 0) { - voxelInjector->addSample(4000); - } else if (i % 4 == 1) { - voxelInjector->addSample(0); - } else if (i % 4 == 2) { - voxelInjector->addSample(-4000); - } else { - voxelInjector->addSample(0); - } - */ - - const float BIG_VOXEL_MIN_SIZE = .01f; - - for (int i = 0; i < 11025; i++) { - - /* - A440 square wave - if (sin(i * 2 * PIE / 50)>=0) { - voxelInjector->addSample(4000); - } else { - voxelInjector->addSample(-4000); - } - */ - - if (_mouseVoxel.s > BIG_VOXEL_MIN_SIZE) { - voxelInjector->addSample(20000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 200)))); - } else { - voxelInjector->addSample(16000 * sin(i / (1.5 * log (_mouseVoxel.s / .0001) * ((i + 11025) / 5512.5)))); //808 - } - } - - //voxelInjector->addSample(32500 * sin(i/(2 * 1 * ((i+5000)/5512.5)))); //80 - //voxelInjector->addSample(20000 * sin(i/(6 * (_mouseVoxel.s/.001) *((i+5512.5)/5512.5)))); //808 - //voxelInjector->addSample(20000 * sin(i/(6 * ((i+5512.5)/5512.5)))); //808 - //voxelInjector->addSample(4000 * sin(i * 2 * PIE /50)); //A440 sine wave - //voxelInjector->addSample(4000 * sin(i * 2 * PIE /50) * sin (i/500)); //A440 sine wave with amplitude modulation - - //FM library - //voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(500*sin((i+1)/200)))); //FM 1 dubstep - //voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(300*sin((i+1)/5.0)))); //FM 2 flange sweep - //voxelInjector->addSample(10000 * sin((i * 2 * PIE) /(500*sin((i+1)/500.0)))); //FM 3 resonant pulse - - AudioInjectionManager::threadInjector(voxelInjector); - } -} - bool Application::maybeEditVoxelUnderCursor() { if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) || Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode)) { @@ -3992,9 +3949,6 @@ bool Application::maybeEditVoxelUnderCursor() { _mouseVoxel.green, _mouseVoxel.blue, Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel)); - - // inject a sound effect - injectVoxelAddedSoundEffect(); // remember the position for drag detection _justEditedVoxel = true; @@ -4028,21 +3982,6 @@ void Application::deleteVoxelUnderCursor() { // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); - AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000); - - if (voxelInjector) { - voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z)); - //voxelInjector->setBearing(0); //straight down the z axis - voxelInjector->setVolume (255); //255 is max, and also default value - - - for (int i = 0; i < 5000; i++) { - voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse - //voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter - } - - AudioInjectionManager::threadInjector(voxelInjector); - } } // remember the position for drag detection _justEditedVoxel = true; @@ -4197,10 +4136,10 @@ void Application::nodeKilled(Node* node) { } void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, - sockaddr senderAddress, bool wasStatsPacket) { + const HifiSockAddr& senderSockAddr, bool wasStatsPacket) { // Attempt to identify the sender from it's address. - Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); + Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr); if (voxelServer) { QUuid nodeUUID = voxelServer->getUUID(); @@ -4214,10 +4153,10 @@ void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t m } } -int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress) { +int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr) { // But, also identify the sender, and keep track of the contained jurisdiction root for this server - Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); + Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr); // parse the incoming stats datas stick it in a temporary object for now, while we // determine which server it belongs to @@ -4270,12 +4209,16 @@ void* Application::networkReceive(void* args) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()"); - sockaddr senderAddress; + HifiSockAddr senderSockAddr; ssize_t bytesReceived; Application* app = Application::getInstance(); while (!app->_stopNetworkReceiveThread) { - if (NodeList::getInstance()->getNodeSocket()->receive(&senderAddress, app->_incomingPacket, &bytesReceived)) { + if (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && + (bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) app->_incomingPacket, + MAX_PACKET_SIZE, + senderSockAddr.getAddressPointer(), + senderSockAddr.getPortPointer()))) { app->_packetCount++; app->_bytesCount += bytesReceived; @@ -4314,11 +4257,11 @@ void* Application::networkReceive(void* args) { } // add this packet to our list of voxel packets and process them on the voxel processing - app->_voxelProcessor.queueReceivedPacket(senderAddress, app->_incomingPacket, bytesReceived); + app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived); break; } case PACKET_TYPE_BULK_AVATAR_DATA: - NodeList::getInstance()->processBulkNodeData(&senderAddress, + NodeList::getInstance()->processBulkNodeData(senderSockAddr, app->_incomingPacket, bytesReceived); getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); @@ -4336,7 +4279,7 @@ void* Application::networkReceive(void* args) { DataServerClient::processMessageFromDataServer(app->_incomingPacket, bytesReceived); break; default: - NodeList::getInstance()->processNodeData(&senderAddress, app->_incomingPacket, bytesReceived); + NodeList::getInstance()->processNodeData(senderSockAddr, app->_incomingPacket, bytesReceived); break; } } diff --git a/interface/src/Application.h b/interface/src/Application.h index c954f036d6..67cb1709c9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -41,13 +41,13 @@ #include "ViewFrustum.h" #include "VoxelFade.h" #include "VoxelEditPacketSender.h" +#include "VoxelHideShowThread.h" #include "VoxelPacketProcessor.h" #include "VoxelSystem.h" #include "VoxelImporter.h" #include "avatar/Avatar.h" #include "avatar/MyAvatar.h" #include "avatar/Profile.h" -#include "avatar/HandControl.h" #include "devices/Faceshift.h" #include "devices/SerialInterface.h" #include "devices/SixenseManager.h" @@ -188,6 +188,10 @@ public: glm::vec2 getViewportDimensions() const{ return glm::vec2(_glWidget->width(),_glWidget->height()); } NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; } void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination); + + /// set a voxel which is to be rendered with a highlight + void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; } + void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } public slots: void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); @@ -231,6 +235,7 @@ private slots: void shrinkMirrorView(); void resetSensors(); + private: void resetCamerasOnResizeGL(Camera& camera, int width, int height); void updateProjectionMatrix(); @@ -272,9 +277,11 @@ private: Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, glm::vec3& eyePosition, QUuid &nodeUUID); bool isLookingAtMyAvatar(Avatar* avatar); - + void renderLookatIndicator(glm::vec3 pointOfInterest); void renderFollowIndicator(); + void renderHighlightVoxel(VoxelDetail voxel); + void updateAvatar(float deltaTime); void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection); void queryVoxels(); @@ -293,7 +300,6 @@ private: bool maybeEditVoxelUnderCursor(); void deleteVoxelUnderCursor(); void eyedropperVoxelUnderCursor(); - void injectVoxelAddedSoundEffect(); void setMenuShortcutsEnabled(bool enabled); @@ -371,8 +377,6 @@ private: int _headMouseX, _headMouseY; - HandControl _handControl; - int _mouseX; int _mouseY; int _mouseDragStartedX; @@ -405,6 +409,9 @@ private: glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel + VoxelDetail _highlightVoxel; + bool _isHighlightVoxel; + VoxelDetail _nudgeVoxel; // details of the voxel to be nudged bool _nudgeStarted; bool _lookingAlongX; @@ -440,8 +447,9 @@ private: bool _stopNetworkReceiveThread; bool _enableProcessVoxelsThread; - VoxelPacketProcessor _voxelProcessor; - VoxelEditPacketSender _voxelEditSender; + VoxelPacketProcessor _voxelProcessor; + VoxelHideShowThread _voxelHideShowThread; + VoxelEditPacketSender _voxelEditSender; unsigned char _incomingPacket[MAX_PACKET_SIZE]; int _packetCount; @@ -462,9 +470,9 @@ private: PieMenu _pieMenu; - int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress); - void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, - sockaddr senderAddress, bool wasStatsPacket); + int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderAddress); + void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, + const HifiSockAddr& senderSockAddr, bool wasStatsPacket); NodeToJurisdictionMap _voxelServerJurisdictions; NodeToVoxelSceneStats _voxelServerSceneStats; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 378d86284a..ceff084ec3 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include "Application.h" @@ -145,9 +144,9 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o // copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES_PER_CHANNEL); - nodeList->getNodeSocket()->send(audioMixer->getActiveSocket(), - dataPacket, - BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); + nodeList->getNodeSocket().writeDatagram((char*) dataPacket, BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes, + audioMixer->getActiveSocket()->getAddress(), + audioMixer->getActiveSocket()->getPort()); interface->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO).updateValue(BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); @@ -289,7 +288,7 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o // add output (@speakers) data just written to the scope _scope->addSamples(1, outputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); _scope->addSamples(2, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - + gettimeofday(&_lastCallbackTime, NULL); } diff --git a/interface/src/DataServerClient.cpp b/interface/src/DataServerClient.cpp index 80be4189b1..33fa59e3e4 100644 --- a/interface/src/DataServerClient.cpp +++ b/interface/src/DataServerClient.cpp @@ -7,10 +7,10 @@ // #include +#include #include #include -#include #include #include "Application.h" @@ -24,7 +24,12 @@ const char MULTI_KEY_VALUE_SEPARATOR = '|'; const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io"; const unsigned short DATA_SERVER_PORT = 3282; -const sockaddr_in DATA_SERVER_SOCKET = socketForHostnameAndHostOrderPort(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT); + + +const HifiSockAddr& DataServerClient::dataServerSockAddr() { + static HifiSockAddr dsSockAddr = HifiSockAddr(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT); + return dsSockAddr; +} void DataServerClient::putValueForKey(const QString& key, const char* value) { QString clientString = Application::getInstance()->getProfile()->getUserString(); @@ -57,7 +62,9 @@ void DataServerClient::putValueForKey(const QString& key, const char* value) { // _unmatchedPackets.insert(std::pair(putPacket, numPacketBytes)); // send this put request to the data server - NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, putPacket, numPacketBytes); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) putPacket, numPacketBytes, + dataServerSockAddr().getAddress(), + dataServerSockAddr().getPort()); } } @@ -96,7 +103,9 @@ void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, co // _unmatchedPackets.insert(std::pair(getPacket, numPacketBytes)); // send the get to the data server - NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, getPacket, numPacketBytes); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) getPacket, numPacketBytes, + dataServerSockAddr().getAddress(), + dataServerSockAddr().getPort()); } } @@ -236,8 +245,8 @@ void DataServerClient::resendUnmatchedPackets() { mapIterator != _unmatchedPackets.end(); ++mapIterator) { // send the unmatched packet to the data server - NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, - mapIterator->first, - mapIterator->second); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) mapIterator->first, mapIterator->second, + dataServerSockAddr().getAddress(), + dataServerSockAddr().getPort()); } } diff --git a/interface/src/DataServerClient.h b/interface/src/DataServerClient.h index 2331cae1e9..b0ccbe13ee 100644 --- a/interface/src/DataServerClient.h +++ b/interface/src/DataServerClient.h @@ -17,6 +17,7 @@ class DataServerClient { public: + static const HifiSockAddr& dataServerSockAddr(); static void putValueForKey(const QString& key, const char* value); static void getClientValueForKey(const QString& key); diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 4f4e694713..c82356ba65 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -18,24 +18,13 @@ #include "renderer/ProgramObject.h" #include "world.h" -uint qHash(const sockaddr& address) { - const sockaddr_in* inetAddress = reinterpret_cast(&address); - if (inetAddress->sin_family != AF_INET) { +uint qHash(const HifiSockAddr& sockAddr) { + if (sockAddr.getAddress().isNull()) { return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash } - return inetAddress->sin_port + qHash(QByteArray::fromRawData( - reinterpret_cast(&inetAddress->sin_addr), sizeof(in_addr))); -} - -bool operator== (const sockaddr& addr1, const sockaddr& addr2) { - return socketMatch(&addr1, &addr2); -} - -static sockaddr getZeroAddress() { - sockaddr addr; - memset(&addr, 0, sizeof(sockaddr)); - addr.sa_family = AF_INET; - return addr; + quint32 address = sockAddr.getAddress().toIPv4Address(); + return sockAddr.getPort() + qHash(QByteArray::fromRawData((char*) &address, + sizeof(address))); } Environment::Environment() @@ -60,14 +49,14 @@ void Environment::init() { _skyFromSpaceProgram = createSkyProgram("Space", _skyFromSpaceUniformLocations); // start off with a default-constructed environment data - _data[getZeroAddress()][0]; + _data[HifiSockAddr()][0]; _initialized = true; } void Environment::resetToDefault() { _data.clear(); - _data[getZeroAddress()][0]; + _data[HifiSockAddr()][0]; } void Environment::renderAtmospheres(Camera& camera) { @@ -159,7 +148,7 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3 return found; } -int Environment::parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int numBytes) { +int Environment::parseData(const HifiSockAddr& senderAddress, unsigned char* sourceBuffer, int numBytes) { // push past the packet header unsigned char* start = sourceBuffer; @@ -175,14 +164,14 @@ int Environment::parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int dataLength = newData.parseData(sourceBuffer, numBytes); // update the mapping by address/ID - _data[*senderAddress][newData.getID()] = newData; + _data[senderAddress][newData.getID()] = newData; sourceBuffer += dataLength; numBytes -= dataLength; } // remove the default mapping, if any - _data.remove(getZeroAddress()); + _data.remove(HifiSockAddr()); return sourceBuffer - start; } diff --git a/interface/src/Environment.h b/interface/src/Environment.h index 1095687ae0..fc572c5e03 100644 --- a/interface/src/Environment.h +++ b/interface/src/Environment.h @@ -12,7 +12,7 @@ #include #include -#include +#include #include "EnvironmentData.h" #include "InterfaceConfig.h" @@ -34,7 +34,7 @@ public: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); - int parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int numBytes); + int parseData(const HifiSockAddr& senderSockAddr, unsigned char* sourceBuffer, int numBytes); private: @@ -71,7 +71,7 @@ private: typedef QHash ServerData; - QHash _data; + QHash _data; QMutex _mutex; }; diff --git a/interface/src/PairingHandler.cpp b/interface/src/PairingHandler.cpp index c220c5f943..718a8c7dc8 100644 --- a/interface/src/PairingHandler.cpp +++ b/interface/src/PairingHandler.cpp @@ -10,6 +10,9 @@ #include #include +#include + +#include #include #include "PairingHandler.h" @@ -27,14 +30,14 @@ PairingHandler* PairingHandler::getInstance() { return instance; } -void PairingHandler::sendPairRequest() { - // grab the node socket from the NodeList singleton - UDPSocket *nodeSocket = NodeList::getInstance()->getNodeSocket(); +void PairingHandler::sendPairRequest() { // prepare the pairing request packet + NodeList* nodeList = NodeList::getInstance(); + // use the getLocalAddress helper to get this client's listening address - int localAddress = getLocalAddress(); + quint32 localAddress = htonl(getHostOrderLocalAddress()); char pairPacket[24] = {}; sprintf(pairPacket, "Find %d.%d.%d.%d:%hu", @@ -42,19 +45,13 @@ void PairingHandler::sendPairRequest() { (localAddress >> 8) & 0xFF, (localAddress >> 16) & 0xFF, (localAddress >> 24) & 0xFF, - NodeList::getInstance()->getSocketListenPort()); + NodeList::getInstance()->getNodeSocket().localPort()); qDebug("Sending pair packet: %s\n", pairPacket); - sockaddr_in pairingServerSocket; - - pairingServerSocket.sin_family = AF_INET; - - // lookup the pairing server IP by the hostname - struct hostent* hostInfo = gethostbyname(PAIRING_SERVER_HOSTNAME); - memcpy(&pairingServerSocket.sin_addr, hostInfo->h_addr_list[0], hostInfo->h_length); - pairingServerSocket.sin_port = htons(PAIRING_SERVER_PORT); + HifiSockAddr pairingServerSocket(PAIRING_SERVER_HOSTNAME, PAIRING_SERVER_PORT); // send the pair request to the pairing server - nodeSocket->send((sockaddr*) &pairingServerSocket, pairPacket, strlen(pairPacket)); + nodeList->getNodeSocket().writeDatagram((char*) pairPacket, strlen(pairPacket), + pairingServerSocket.getAddress(), pairingServerSocket.getPort()); } diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp new file mode 100644 index 0000000000..bb64dd5b08 --- /dev/null +++ b/interface/src/VoxelHideShowThread.cpp @@ -0,0 +1,46 @@ +// +// VoxelHideShowThread.cpp +// interface +// +// Created by Brad Hefta-Gaub on 12/1/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded interface thread for hiding and showing voxels in the local tree. +// + +#include +#include +#include +#include + +#include "Menu.h" +#include "VoxelHideShowThread.h" + +VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) : + _theSystem(theSystem) { +} + +bool VoxelHideShowThread::process() { + const uint64_t MSECS_TO_USECS = 1000; + const uint64_t SECS_TO_USECS = 1000 * MSECS_TO_USECS; + const uint64_t FRAME_RATE = 60; + const uint64_t USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps + + uint64_t start = usecTimestampNow(); + _theSystem->checkForCulling(); + uint64_t end = usecTimestampNow(); + uint64_t elapsed = end - start; + + bool showExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging); + if (showExtraDebugging && elapsed > USECS_PER_FRAME) { + printf("VoxelHideShowThread::process()... checkForCulling took %llu\n", elapsed); + } + + if (isStillRunning()) { + if (elapsed < USECS_PER_FRAME) { + uint64_t sleepFor = USECS_PER_FRAME - elapsed; + usleep(sleepFor); + } + } + return isStillRunning(); // keep running till they terminate us +} diff --git a/interface/src/VoxelHideShowThread.h b/interface/src/VoxelHideShowThread.h new file mode 100644 index 0000000000..22df6c299c --- /dev/null +++ b/interface/src/VoxelHideShowThread.h @@ -0,0 +1,31 @@ +// +// VoxelHideShowThread.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 12/1/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded voxel persistence +// + +#ifndef __interface__VoxelHideShowThread__ +#define __interface__VoxelHideShowThread__ + +#include +#include "VoxelSystem.h" + +/// Generalized threaded processor for handling received inbound packets. +class VoxelHideShowThread : public virtual GenericThread { +public: + + VoxelHideShowThread(VoxelSystem* theSystem); + +protected: + /// Implements generic processing behavior for this thread. + virtual bool process(); + +private: + VoxelSystem* _theSystem; +}; + +#endif // __interface__VoxelHideShowThread__ diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp index b2374bc711..a591fbdbaa 100644 --- a/interface/src/VoxelPacketProcessor.cpp +++ b/interface/src/VoxelPacketProcessor.cpp @@ -14,7 +14,7 @@ #include "Menu.h" #include "VoxelPacketProcessor.h" -void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { +void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "VoxelPacketProcessor::processPacket()"); @@ -39,7 +39,7 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* // then process any remaining bytes as if it was another packet if (packetData[0] == PACKET_TYPE_VOXEL_STATS) { - int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderAddress); + int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderSockAddr); wasStatsPacket = true; if (messageLength > statsMessageLength) { packetData += statsMessageLength; @@ -54,13 +54,12 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* } // fall through to piggyback message if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { - - app->trackIncomingVoxelPacket(packetData, messageLength, senderAddress, wasStatsPacket); + app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket); - Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); - if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) { + Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr); + if (voxelServer && *voxelServer->getActiveSocket() == senderSockAddr) { if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) { - app->_environment.parseData(&senderAddress, packetData, messageLength); + app->_environment.parseData(senderSockAddr, packetData, messageLength); } else { app->_voxels.setDataSourceUUID(voxelServer->getUUID()); app->_voxels.parseData(packetData, messageLength); diff --git a/interface/src/VoxelPacketProcessor.h b/interface/src/VoxelPacketProcessor.h index 87aae397d5..e8e77e6895 100644 --- a/interface/src/VoxelPacketProcessor.h +++ b/interface/src/VoxelPacketProcessor.h @@ -17,6 +17,6 @@ /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() class VoxelPacketProcessor : public ReceivedPacketProcessor { protected: - virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); + virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength); }; #endif // __shared__VoxelPacketProcessor__ diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 19240f7f17..f5a2a99ffb 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -563,13 +563,15 @@ bool VoxelSystem::readFromSchematicFile(const char* filename) { } int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { + bool showTimingDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData()",showTimingDetails); + unsigned char command = *sourceBuffer; int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); switch(command) { case PACKET_TYPE_VOXEL_DATA: { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "readBitstreamToTree()"); - + PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PACKET_TYPE_VOXEL_DATA part...",showTimingDetails); + unsigned char* dataAt = sourceBuffer + numBytesPacketHeader; VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt)); @@ -611,7 +613,8 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { VoxelPacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) { - qDebug("Got Packet color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d" + qDebug("VoxelSystem::parseData() ... Got Packet Section" + " color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d" " subsection:%d sectionLength:%d uncompressed:%d\n", debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize()); @@ -627,12 +630,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { } break; } - - if (!_useFastVoxelPipeline || _writeRenderFullVBO) { setupNewVoxelsForDrawing(); } else { - checkForCulling(); setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY); } @@ -661,8 +661,6 @@ void VoxelSystem::setupNewVoxelsForDrawing() { _inSetupNewVoxelsForDrawing = true; - checkForCulling(); // check for out of view and deleted voxels... - bool didWriteFullVBO = _writeRenderFullVBO; if (_tree->isDirty()) { static char buffer[64] = { 0 }; @@ -747,68 +745,44 @@ void VoxelSystem::checkForCulling() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()"); uint64_t start = usecTimestampNow(); - uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000; - // These items used to be menu options, we are not defaulting to and only supporting these modes. - bool constantCulling = true; - bool performHideOutOfViewLogic = true; - bool performRemoveOutOfViewLogic = false; - // If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view - if (constantCulling || ( - (sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) - && !isViewChanging() - ) - ) { - // When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove - // them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which - // can be expensive). - if (performHideOutOfViewLogic) { - - // track how long its been since we were last moving. If we have recently moved then only use delta frustums, if - // it's been a long time since we last moved, then go ahead and do a full frustum cull. - if (isViewChanging()) { - _lastViewIsChanging = start; - } - uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000; - - bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)); - - // These has changed events will occur before we stop. So we need to remember this for when we finally have stopped - // moving long enough to be enoughTime - if (hasViewChanged()) { - _hasRecentlyChanged = true; - } - - // If we have recently changed, but it's been enough time since we last moved, then we will do a full frustum - // hide/show culling pass - bool forceFullFrustum = enoughTime && _hasRecentlyChanged; - - // in hide mode, we only track the full frustum culls, because we don't care about the partials. - if (forceFullFrustum) { - _lastViewCulling = start; - _hasRecentlyChanged = false; - } - - hideOutOfView(forceFullFrustum); - - if (forceFullFrustum) { - uint64_t endViewCulling = usecTimestampNow(); - _lastViewCullingElapsed = (endViewCulling - start) / 1000; - } - - } else if (performRemoveOutOfViewLogic) { - _lastViewCulling = start; - removeOutOfView(); - uint64_t endViewCulling = usecTimestampNow(); - _lastViewCullingElapsed = (endViewCulling - start) / 1000; - } - - // Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So, - // we should consider putting this someplace else... as this might be able to occur less frequently, and save us on - // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. - cleanupRemovedVoxels(); + // track how long its been since we were last moving. If we have recently moved then only use delta frustums, if + // it's been a long time since we last moved, then go ahead and do a full frustum cull. + if (isViewChanging()) { + _lastViewIsChanging = start; } + uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000; + + bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)); + + // These has changed events will occur before we stop. So we need to remember this for when we finally have stopped + // moving long enough to be enoughTime + if (hasViewChanged()) { + _hasRecentlyChanged = true; + } + + // If we have recently changed, but it's been enough time since we last moved, then we will do a full frustum + // hide/show culling pass + bool forceFullFrustum = enoughTime && _hasRecentlyChanged; + + // in hide mode, we only track the full frustum culls, because we don't care about the partials. + if (forceFullFrustum) { + _lastViewCulling = start; + _hasRecentlyChanged = false; + } + + hideOutOfView(forceFullFrustum); + + if (forceFullFrustum) { + uint64_t endViewCulling = usecTimestampNow(); + _lastViewCullingElapsed = (endViewCulling - start) / 1000; + } + + // Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So, + // we should consider putting this someplace else... as this might be able to occur less frequently, and save us on + // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. + cleanupRemovedVoxels(); uint64_t sinceLastAudit = (start - _lastAudit) / 1000; @@ -1456,9 +1430,9 @@ void VoxelSystem::clearAllNodesBufferIndex() { } } -bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* node, void* extraData) { +bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData) { _nodeCount++; - node->setDirtyBit(); + element->setDirtyBit(); return true; } @@ -1542,6 +1516,39 @@ void VoxelSystem::falseColorizeInView() { setupNewVoxelsForDrawing(); } +class VoxelAndPoint { +public: + VoxelTreeElement* voxel; + glm::vec3 point; +}; + +// Find the smallest colored voxel enclosing a point (if there is one) +bool VoxelSystem::getVoxelEnclosingOperation(OctreeElement* element, void* extraData) { + VoxelTreeElement* voxel = (VoxelTreeElement*)element; + VoxelAndPoint* args = (VoxelAndPoint*) extraData; + AABox voxelBox = voxel->getAABox(); + if (voxelBox.contains(args->point)) { + if (voxel->isColored() && voxel->isLeaf()) { + // we've reached a solid leaf containing the point, return the node. + args->voxel = voxel; + return false; + } + } else { + // The point is not inside this voxel, so stop recursing. + return false; + } + return true; // keep looking +} + +VoxelTreeElement* VoxelSystem::getVoxelEnclosing(const glm::vec3& point) { + VoxelAndPoint voxelAndPoint; + voxelAndPoint.point = point; + voxelAndPoint.voxel = NULL; + _tree->recurseTreeWithOperation(getVoxelEnclosingOperation, (void*) &voxelAndPoint); + return voxelAndPoint.voxel; +} + + // helper classes and args for falseColorizeBySource class groupColor { public: diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 5499838edf..28e66d11dc 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -40,6 +39,9 @@ struct VoxelShaderVBOData class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook, public NodeListHook, public DomainChangeListener { Q_OBJECT + + friend class VoxelHideShowThread; + public: VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM); ~VoxelSystem(); @@ -114,6 +116,8 @@ public: virtual void domainChanged(QString domain); bool treeIsBusy() const { return _treeIsBusy; } + + VoxelTreeElement* getVoxelEnclosing(const glm::vec3& point); signals: void importSize(float x, float y, float z); @@ -173,26 +177,27 @@ private: // Operation functions for tree recursion methods static int _nodeCount; - static bool randomColorOperation(OctreeElement* node, void* extraData); - static bool falseColorizeRandomOperation(OctreeElement* node, void* extraData); - static bool trueColorizeOperation(OctreeElement* node, void* extraData); - static bool falseColorizeInViewOperation(OctreeElement* node, void* extraData); - static bool falseColorizeDistanceFromViewOperation(OctreeElement* node, void* extraData); - static bool getDistanceFromViewRangeOperation(OctreeElement* node, void* extraData); - static bool removeOutOfViewOperation(OctreeElement* node, void* extraData); - static bool falseColorizeRandomEveryOtherOperation(OctreeElement* node, void* extraData); - static bool collectStatsForTreesAndVBOsOperation(OctreeElement* node, void* extraData); - static bool falseColorizeOccludedOperation(OctreeElement* node, void* extraData); - static bool falseColorizeSubTreeOperation(OctreeElement* node, void* extraData); - static bool falseColorizeOccludedV2Operation(OctreeElement* node, void* extraData); - static bool falseColorizeBySourceOperation(OctreeElement* node, void* extraData); - static bool killSourceVoxelsOperation(OctreeElement* node, void* extraData); - static bool forceRedrawEntireTreeOperation(OctreeElement* node, void* extraData); - static bool clearAllNodesBufferIndexOperation(OctreeElement* node, void* extraData); - static bool hideOutOfViewOperation(OctreeElement* node, void* extraData); - static bool hideAllSubTreeOperation(OctreeElement* node, void* extraData); - static bool showAllSubTreeOperation(OctreeElement* node, void* extraData); - static bool showAllLocalVoxelsOperation(OctreeElement* node, void* extraData); + static bool randomColorOperation(OctreeElement* element, void* extraData); + static bool falseColorizeRandomOperation(OctreeElement* element, void* extraData); + static bool trueColorizeOperation(OctreeElement* element, void* extraData); + static bool falseColorizeInViewOperation(OctreeElement* element, void* extraData); + static bool falseColorizeDistanceFromViewOperation(OctreeElement* element, void* extraData); + static bool getDistanceFromViewRangeOperation(OctreeElement* element, void* extraData); + static bool removeOutOfViewOperation(OctreeElement* element, void* extraData); + static bool falseColorizeRandomEveryOtherOperation(OctreeElement* element, void* extraData); + static bool collectStatsForTreesAndVBOsOperation(OctreeElement* element, void* extraData); + static bool falseColorizeOccludedOperation(OctreeElement* element, void* extraData); + static bool falseColorizeSubTreeOperation(OctreeElement* element, void* extraData); + static bool falseColorizeOccludedV2Operation(OctreeElement* element, void* extraData); + static bool falseColorizeBySourceOperation(OctreeElement* element, void* extraData); + static bool killSourceVoxelsOperation(OctreeElement* element, void* extraData); + static bool forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData); + static bool clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData); + static bool hideOutOfViewOperation(OctreeElement* element, void* extraData); + static bool hideAllSubTreeOperation(OctreeElement* element, void* extraData); + static bool showAllSubTreeOperation(OctreeElement* element, void* extraData); + static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData); + static bool getVoxelEnclosingOperation(OctreeElement* element, void* extraData); int updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, bool forceDraw); int forceRemoveNodeFromArrays(VoxelTreeElement* node); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index f733a40c9f..4af627881a 100755 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -24,7 +24,10 @@ Hand::Hand(Avatar* owningAvatar) : _raveGloveInitialized(false), _owningAvatar(owningAvatar), _renderAlpha(1.0), - _ballColor(0.0, 0.0, 0.4) + _ballColor(0.0, 0.0, 0.4), + _collisionCenter(0,0,0), + _collisionAge(0), + _collisionDuration(0) { // initialize all finger particle emitters with an invalid id as default for (int f = 0; f< NUM_FINGERS; f ++ ) { @@ -51,6 +54,10 @@ void Hand::reset() { void Hand::simulate(float deltaTime, bool isMine) { + if (_collisionAge > 0.f) { + _collisionAge += deltaTime; + } + calculateGeometry(); if (_isRaveGloveActive) { @@ -67,29 +74,77 @@ void Hand::simulate(float deltaTime, bool isMine) { for (size_t i = 0; i < getNumPalms(); ++i) { PalmData& palm = getPalms()[i]; if (palm.isActive()) { - FingerData& finger = palm.getFingers()[0]; - glm::vec3 newVoxelPosition = finger.getTipPosition(); + FingerData& finger = palm.getFingers()[0]; // Sixense has only one finger + glm::vec3 fingerTipPosition = finger.getTipPosition(); if (palm.getControllerButtons() & BUTTON_1) { - if (glm::length(newVoxelPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) { + if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) { QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value(); - Application::getInstance()->makeVoxel(newVoxelPosition, + Application::getInstance()->makeVoxel(fingerTipPosition, FINGERTIP_VOXEL_SIZE, paintColor.red(), paintColor.green(), paintColor.blue(), true); - _lastFingerAddVoxel = newVoxelPosition; + _lastFingerAddVoxel = fingerTipPosition; } } else if (palm.getControllerButtons() & BUTTON_2) { - if (glm::length(newVoxelPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) { - Application::getInstance()->removeVoxel(newVoxelPosition, FINGERTIP_VOXEL_SIZE); - _lastFingerDeleteVoxel = newVoxelPosition; + if (glm::length(fingerTipPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) { + Application::getInstance()->removeVoxel(fingerTipPosition, FINGERTIP_VOXEL_SIZE); + _lastFingerDeleteVoxel = fingerTipPosition; + } + } + // Check if the finger is intersecting with a voxel in the client voxel tree + VoxelTreeElement* fingerNode = Application::getInstance()->getVoxels()->getVoxelEnclosing( + glm::vec3(fingerTipPosition / (float)TREE_SCALE)); + if (fingerNode) { + if (!palm.getIsCollidingWithVoxel()) { + // Collision has just started + palm.setIsCollidingWithVoxel(true); + handleVoxelCollision(&palm, fingerTipPosition, fingerNode, deltaTime); + // Set highlight voxel + VoxelDetail voxel; + glm::vec3 pos = fingerNode->getCorner(); + voxel.x = pos.x; + voxel.y = pos.y; + voxel.z = pos.z; + voxel.s = fingerNode->getScale(); + voxel.red = fingerNode->getColor()[0]; + voxel.green = fingerNode->getColor()[1]; + voxel.blue = fingerNode->getColor()[2]; + Application::getInstance()->setHighlightVoxel(voxel); + Application::getInstance()->setIsHighlightVoxel(true); + } + } else { + if (palm.getIsCollidingWithVoxel()) { + // Collision has just ended + palm.setIsCollidingWithVoxel(false); + Application::getInstance()->setIsHighlightVoxel(false); } } } } } +void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime) { + // + // Collision between finger and a voxel plays sound + // + float volume = glm::length(palm->getVelocity()); + float duration = volume; + _collisionCenter = fingerTipPosition; + _collisionAge = deltaTime; + _collisionDuration = duration; + int voxelBrightness = voxel->getColor()[0] + voxel->getColor()[1] + voxel->getColor()[2]; + float frequency = 100.f + (voxelBrightness * 2.f); // Hz + // Play a sound + Application::getInstance()->getAudio()->startCollisionSound(volume, + frequency, + 0.25, + 0.995f, + false); + +} + void Hand::calculateGeometry() { const glm::vec3 leapHandsOffsetFromFace(0.0, -0.2, -0.3); // place the hand in front of the face where we can see it @@ -105,7 +160,7 @@ void Hand::calculateGeometry() { for (size_t f = 0; f < palm.getNumFingers(); ++f) { FingerData& finger = palm.getFingers()[f]; if (finger.isActive()) { - const float standardBallRadius = 0.005f; + const float standardBallRadius = 0.010f; _leapFingerTipBalls.resize(_leapFingerTipBalls.size() + 1); HandBall& ball = _leapFingerTipBalls.back(); ball.rotation = _baseOrientation; @@ -113,6 +168,7 @@ void Hand::calculateGeometry() { ball.radius = standardBallRadius; ball.touchForce = 0.0; ball.isCollidable = true; + ball.isColliding = false; } } } @@ -134,6 +190,7 @@ void Hand::calculateGeometry() { ball.radius = standardBallRadius; ball.touchForce = 0.0; ball.isCollidable = true; + ball.isColliding = false; } } } @@ -184,6 +241,21 @@ void Hand::render() { } } + // If hand/voxel collision has happened, render a little expanding sphere + if (_collisionAge > 0.f) { + float opacity = glm::clamp(1.f - (_collisionAge / _collisionDuration), 0.f, 1.f); + glColor4f(1, 0, 0, 0.5 * opacity); + glPushMatrix(); + glTranslatef(_collisionCenter.x, _collisionCenter.y, _collisionCenter.z); + glutSolidSphere(_collisionAge * 0.25f, 20, 20); + glPopMatrix(); + if (_collisionAge > _collisionDuration) { + _collisionAge = 0.f; + } + } + + + // If hand controller buttons pressed, render stuff as needed if (getPalms().size() > 0) { for (size_t i = 0; i < getPalms().size(); ++i) { @@ -299,8 +371,11 @@ void Hand::renderLeapHands() { // Draw the leap balls for (size_t i = 0; i < _leapFingerTipBalls.size(); i++) { if (alpha > 0.0f) { - glColor4f(handColor.r, handColor.g, handColor.b, alpha); - + if (_leapFingerTipBalls[i].isColliding) { + glColor4f(handColor.r, 0, 0, alpha); + } else { + glColor4f(handColor.r, handColor.g, handColor.b, alpha); + } glPushMatrix(); glTranslatef(_leapFingerTipBalls[i].position.x, _leapFingerTipBalls[i].position.y, _leapFingerTipBalls[i].position.z); glutSolidSphere(_leapFingerTipBalls[i].radius, 20.0f, 20.0f); diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index aea03f4cc1..e4e9ad4b50 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -45,6 +45,7 @@ public: glm::vec3 velocity; // the velocity of the ball float radius; // the radius of the ball bool isCollidable; // whether or not the ball responds to collisions + bool isColliding; // ball is currently colliding float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball }; @@ -83,6 +84,12 @@ private: std::vector _leapFingerRootBalls; glm::vec3 _lastFingerAddVoxel, _lastFingerDeleteVoxel; + bool _isCollidingWithVoxel; + VoxelDetail _collidingVoxel; + + glm::vec3 _collisionCenter; + float _collisionAge; + float _collisionDuration; // private methods void setLeapHands(const std::vector& handPositions, @@ -93,6 +100,8 @@ private: void renderLeapHands(); void renderLeapFingerTrails(); void calculateGeometry(); + + void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime); }; #endif diff --git a/interface/src/avatar/HandControl.cpp b/interface/src/avatar/HandControl.cpp deleted file mode 100644 index 596f971bd2..0000000000 --- a/interface/src/avatar/HandControl.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// -// HandControl.cpp -// interface -// -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include "HandControl.h" - -// this class takes mouse movements normalized within the screen -// dimensions and uses those to determine avatar hand movements, as well -// as states for ramping up and ramping down the amplitude of such movements. -// -// This class might expand to accommodate 3D input devices -// - -HandControl::HandControl() { - - _enabled = false; - _width = 0; - _height = 0; - _startX = 0; - _startY = 0; - _x = 0; - _y = 0; - _lastX = 0; - _lastY = 0; - _velocityX = 0; - _velocityY = 0; - _rampUpRate = 0.05; - _rampDownRate = 0.02; - _envelope = 0.0f; -} - -void HandControl::setScreenDimensions(int width, int height) { - _width = width; - _height = height; - _startX = _width / 2; - _startY = _height / 2; -} - -void HandControl::update(int x, int y) { - _lastX = _x; - _lastY = _y; - _x = x; - _y = y; - _velocityX = _x - _lastX; - _velocityY = _y - _lastY; - - // if the mouse is moving, ramp up the envelope to increase amplitude of hand movement... - if ((_velocityX != 0) - || (_velocityY != 0)) { - _enabled = true; - if (_envelope < 1.0) { - _envelope += _rampUpRate; - if (_envelope >= 1.0) { - _envelope = 1.0; - } - } - } - - // if not enabled ramp down the envelope to decrease amplitude of hand movement... - if (! _enabled) { - if (_envelope > 0.0) { - _envelope -= _rampDownRate; - if (_envelope <= 0.0) { - _startX = _width / 2; - _startY = _height / 2; - _envelope = 0.0; - } - } - } - - _leftRight = 0.0; - _downUp = 0.0; - _backFront = 0.0; - - // if envelope is greater than zero, apply mouse movement to values to be output - if (_envelope > 0.0) { - _leftRight += ((_x - _startX) / (float)_width ) * _envelope; - _downUp += ((_y - _startY) / (float)_height) * _envelope; - } -} - -glm::vec3 HandControl::getValues() { - return glm::vec3(_leftRight, _downUp, _backFront); -} - -void HandControl::stop() { - _enabled = false; -} - diff --git a/interface/src/avatar/HandControl.h b/interface/src/avatar/HandControl.h deleted file mode 100644 index b2abef48bb..0000000000 --- a/interface/src/avatar/HandControl.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// HandControl.h -// interface -// -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__HandControl__ -#define __interface__HandControl__ - -#include - -class HandControl { -public: - HandControl(); - void setScreenDimensions(int width, int height); - void update( int x, int y ); - glm::vec3 getValues(); - void stop(); - -private: - bool _enabled; - int _width; - int _height; - int _startX; - int _startY; - int _x; - int _y; - int _lastX; - int _lastY; - int _velocityX; - int _velocityY; - float _rampUpRate; - float _rampDownRate; - float _envelope; - float _leftRight; - float _downUp; - float _backFront; -}; - -#endif diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 8055e8112d..1ee78126f8 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -32,7 +32,6 @@ void SixenseManager::update(float deltaTime) { } MyAvatar* avatar = Application::getInstance()->getAvatar(); Hand& hand = avatar->getHand(); - hand.getPalms().clear(); int maxControllers = sixenseGetMaxControllers(); for (int i = 0; i < maxControllers; i++) { @@ -42,45 +41,63 @@ void SixenseManager::update(float deltaTime) { sixenseControllerData data; sixenseGetNewestData(i, &data); - // Set palm position and normal based on Hydra position/orientation - PalmData palm(&hand); - palm.setActive(true); - glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]); + //printf("si: %i\n", data.controller_index); - // Compute current velocity from position change - palm.setVelocity((position - palm.getPosition()) / deltaTime); + // Set palm position and normal based on Hydra position/orientation + + // Either find a palm matching the sixense controller, or make a new one + PalmData* palm; + bool foundHand = false; + for (int j = 0; j < hand.getNumPalms(); j++) { + if (hand.getPalms()[j].getSixenseID() == data.controller_index) { + palm = &hand.getPalms()[j]; + foundHand = true; + } + } + if (!foundHand) { + PalmData newPalm(&hand); + hand.getPalms().push_back(newPalm); + palm = &hand.getPalms()[hand.getNumPalms() - 1]; + palm->setSixenseID(data.controller_index); + printf("Found new Sixense controller, ID %i\n", data.controller_index); + } + + palm->setActive(true); // Read controller buttons and joystick into the hand - palm.setControllerButtons(data.buttons); - palm.setTrigger(data.trigger); - palm.setJoystick(data.joystick_x, data.joystick_y); - + palm->setControllerButtons(data.buttons); + palm->setTrigger(data.trigger); + palm->setJoystick(data.joystick_x, data.joystick_y); + + glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]); // Adjust for distance between acquisition 'orb' and the user's torso // (distance to the right of body center, distance below torso, distance behind torso) const glm::vec3 SPHERE_TO_TORSO(-250.f, -300.f, -300.f); position = SPHERE_TO_TORSO + position; - palm.setRawPosition(position); - glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]); - // Rotate about controller + // Compute current velocity from position change + palm->setVelocity((position - palm->getRawPosition()) / deltaTime / 1000.f); // meters/sec + palm->setRawPosition(position); + + // Rotation of Palm + glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]); rotation = glm::angleAxis(180.0f, 0.f, 1.f, 0.f) * rotation; const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f); - palm.setRawNormal(rotation * PALM_VECTOR); + palm->setRawNormal(rotation * PALM_VECTOR); // initialize the "finger" based on the direction - FingerData finger(&palm, &hand); + FingerData finger(palm, &hand); finger.setActive(true); finger.setRawRootPosition(position); - const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, 100.0f); + const float FINGER_LENGTH = 300.0f; // Millimeters + const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH); finger.setRawTipPosition(position + rotation * FINGER_VECTOR); // three fingers indicates to the skeleton that we have enough data to determine direction - palm.getFingers().clear(); - palm.getFingers().push_back(finger); - palm.getFingers().push_back(finger); - palm.getFingers().push_back(finger); - - hand.getPalms().push_back(palm); + palm->getFingers().clear(); + palm->getFingers().push_back(finger); + palm->getFingers().push_back(finger); + palm->getFingers().push_back(finger); } #endif } diff --git a/interface/src/starfield/Generator.cpp b/interface/src/starfield/Generator.cpp index 63a11f8f11..d8d38a61a6 100644 --- a/interface/src/starfield/Generator.cpp +++ b/interface/src/starfield/Generator.cpp @@ -24,22 +24,24 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit, vertices->clear(); vertices->reserve(limit); - const unsigned MILKY_WAY_WIDTH = 16.0; // width in degrees of one half of the Milky Way - const float MILKY_WAY_INCLINATION = 30.0f; // angle of Milky Way from horizontal in degrees - const float MILKY_WAY_RATIO = 0.6; + const unsigned MILKY_WAY_WIDTH = 12.0; // width in degrees of one half of the Milky Way + const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees + const float MILKY_WAY_RATIO = 0.4; const unsigned NUM_DEGREES = 360; for(int star = 0; star < floor(limit * (1 - MILKY_WAY_RATIO)); ++star) { float azimuth, altitude; azimuth = (((float)rand() / (float) RAND_MAX) * NUM_DEGREES) - (NUM_DEGREES / 2); altitude = (acos((2.0f * ((float)rand() / (float)RAND_MAX)) - 1.0f) / PI_OVER_180) + 90; - vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION))); } for(int star = 0; star < ceil(limit * MILKY_WAY_RATIO); ++star) { float azimuth = ((float)rand() / (float) RAND_MAX) * NUM_DEGREES; - float altitude = asin((float)rand() / (float) RAND_MAX * 2 - 1) * MILKY_WAY_WIDTH; + float altitude = powf(randFloat()*0.5f, 2.f)/0.25f * MILKY_WAY_WIDTH; + if (randFloat() > 0.5f) { + altitude *= -1.f; + } // we need to rotate the Milky Way band to the correct orientation in the sky // convert from spherical coordinates to cartesian, rotate the point and then convert back. @@ -71,8 +73,15 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit, // 0 = completely black & white // 1 = very colorful unsigned Generator::computeStarColor(float colorization) { - unsigned char red = rand() % 256; - unsigned char green = round((red * (1 - colorization)) + ((rand() % 256) * colorization)); - unsigned char blue = round((red * (1 - colorization)) + ((rand() % 256) * colorization)); - return red | green << 8 | blue << 16; + unsigned char red, green, blue; + if (randFloat() < 0.3f) { + // A few stars are colorful + red = 2 + (rand() % 254); + green = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization)); + blue = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization)); + } else { + // Most stars are dimmer and white + red = green = blue = 2 + (rand() % 128); + } + return red | (green << 8) | (blue << 16); } \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectionManager.cpp b/libraries/audio/src/AudioInjectionManager.cpp deleted file mode 100644 index 16ace5bbb6..0000000000 --- a/libraries/audio/src/AudioInjectionManager.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// AudioInjectionManager.cpp -// hifi -// -// Created by Stephen Birarda on 5/16/13. -// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. -// - -#include - -#include "SharedUtil.h" -#include "NodeList.h" -#include "NodeTypes.h" -#include "Node.h" -#include "PacketHeaders.h" - -#include "AudioInjectionManager.h" - -UDPSocket* AudioInjectionManager::_injectorSocket = NULL; -sockaddr AudioInjectionManager::_destinationSocket; -bool AudioInjectionManager::_isDestinationSocketExplicit = false; -AudioInjector* AudioInjectionManager::_injectors[50] = {}; - -AudioInjector* AudioInjectionManager::injectorWithCapacity(int capacity) { - for (int i = 0; i < MAX_CONCURRENT_INJECTORS; i++) { - if (!_injectors[i]) { - _injectors[i] = new AudioInjector(capacity); - return _injectors[i]; - } - } - - return NULL; -} - -void AudioInjectionManager::setDestinationSocket(sockaddr& destinationSocket) { - _destinationSocket = destinationSocket; - _isDestinationSocketExplicit = true; -} - -void* AudioInjectionManager::injectAudioViaThread(void* args) { - AudioInjector* injector = (AudioInjector*) args; - - // if we don't have an injectorSocket then grab the one from the node list - if (!_injectorSocket) { - _injectorSocket = NodeList::getInstance()->getNodeSocket(); - } - - // if we don't have an explicit destination socket then pull active socket for current audio mixer from node list - if (!_isDestinationSocketExplicit) { - Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); - if (audioMixer && audioMixer->getActiveSocket()) { - _destinationSocket = *audioMixer->getActiveSocket(); - } else { - pthread_exit(0); - } - } - - injector->injectAudio(_injectorSocket, &_destinationSocket); - - // if this an injector inside the injection manager's array we're responsible for deletion - for (int i = 0; i < MAX_CONCURRENT_INJECTORS; i++) { - if (_injectors[i] == injector) { - // pointer matched - delete this injector - delete injector; - - // set the pointer to NULL so we can reuse this spot - _injectors[i] = NULL; - } - } - - pthread_exit(0); -} - -void AudioInjectionManager::threadInjector(AudioInjector* injector) { - pthread_t audioInjectThread; - pthread_create(&audioInjectThread, NULL, injectAudioViaThread, (void*) injector); -} \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectionManager.h b/libraries/audio/src/AudioInjectionManager.h deleted file mode 100644 index 5f56663a78..0000000000 --- a/libraries/audio/src/AudioInjectionManager.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// AudioInjectionManager.h -// hifi -// -// Created by Stephen Birarda on 5/16/13. -// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. -// - -#ifndef __hifi__AudioInjectionManager__ -#define __hifi__AudioInjectionManager__ - -#include - -#include "UDPSocket.h" -#include "AudioInjector.h" - -const int MAX_CONCURRENT_INJECTORS = 50; - -class AudioInjectionManager { -public: - static AudioInjector* injectorWithCapacity(int capacity); - - static void threadInjector(AudioInjector* injector); - - static void setInjectorSocket(UDPSocket* injectorSocket) { _injectorSocket = injectorSocket;} - static void setDestinationSocket(sockaddr& destinationSocket); -private: - static void* injectAudioViaThread(void* args); - - static UDPSocket* _injectorSocket; - static sockaddr _destinationSocket; - static bool _isDestinationSocketExplicit; - static AudioInjector* _injectors[MAX_CONCURRENT_INJECTORS]; -}; - -#endif /* defined(__hifi__AudioInjectionManager__) */ diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp deleted file mode 100644 index 2e58aa13c8..0000000000 --- a/libraries/audio/src/AudioInjector.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// -// AudioInjector.cpp -// hifi -// -// Created by Stephen Birarda on 4/23/13. -// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. -// - -#include -#include -#include - -#include -#include -#include -#include - -#include "AudioInjector.h" - -AudioInjector::AudioInjector(int maxNumSamples) : - _streamIdentifier(QUuid::createUuid()), - _numTotalSamples(maxNumSamples), - _position(0.0f, 0.0f, 0.0f), - _orientation(), - _radius(0.0f), - _volume(MAX_INJECTOR_VOLUME), - _indexOfNextSlot(0), - _isInjectingAudio(false) -{ - _audioSampleArray = new int16_t[maxNumSamples]; - memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t)); -} - -AudioInjector::~AudioInjector() { - delete[] _audioSampleArray; -} - -void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destinationSocket) { - if (_audioSampleArray && _indexOfNextSlot > 0) { - _isInjectingAudio = true; - - timeval startTime; - - // calculate the number of bytes required for additional data - int leadingBytes = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_INJECT_AUDIO) - + NUM_BYTES_RFC4122_UUID - + NUM_BYTES_RFC4122_UUID - + sizeof(_position) - + sizeof(_orientation) - + sizeof(_radius) - + sizeof(_volume); - - unsigned char dataPacket[(BUFFER_LENGTH_SAMPLES_PER_CHANNEL * sizeof(int16_t)) + leadingBytes]; - - unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, PACKET_TYPE_INJECT_AUDIO); - - // copy the UUID for the owning node - QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); - memcpy(currentPacketPtr, rfcUUID.constData(), rfcUUID.size()); - currentPacketPtr += rfcUUID.size(); - - // copy the stream identifier - QByteArray rfcStreamIdentifier = _streamIdentifier.toRfc4122(); - memcpy(currentPacketPtr, rfcStreamIdentifier.constData(), rfcStreamIdentifier.size()); - currentPacketPtr += rfcStreamIdentifier.size(); - - memcpy(currentPacketPtr, &_position, sizeof(_position)); - currentPacketPtr += sizeof(_position); - - memcpy(currentPacketPtr, &_orientation, sizeof(_orientation)); - currentPacketPtr += sizeof(_orientation); - - memcpy(currentPacketPtr, &_radius, sizeof(_radius)); - currentPacketPtr += sizeof(_radius); - - *currentPacketPtr = _volume; - currentPacketPtr++; - - gettimeofday(&startTime, NULL); - int nextFrame = 0; - - for (int i = 0; i < _numTotalSamples; i += BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { - int usecToSleep = usecTimestamp(&startTime) + (nextFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); - if (usecToSleep > 0) { - usleep(usecToSleep); - } - - int numSamplesToCopy = BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - - if (_numTotalSamples - i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { - numSamplesToCopy = _numTotalSamples - i; - memset(currentPacketPtr + numSamplesToCopy, - 0, - BUFFER_LENGTH_BYTES_PER_CHANNEL - (numSamplesToCopy * sizeof(int16_t))); - } - - memcpy(currentPacketPtr, _audioSampleArray + i, numSamplesToCopy * sizeof(int16_t)); - - injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket)); - } - - _isInjectingAudio = false; - } -} - -void AudioInjector::addSample(const int16_t sample) { - if (_indexOfNextSlot != _numTotalSamples) { - // only add this sample if we actually have space for it - _audioSampleArray[_indexOfNextSlot++] = sample; - } -} - -void AudioInjector::addSamples(int16_t* sampleBuffer, int numSamples) { - if (_audioSampleArray + _indexOfNextSlot + numSamples <= _audioSampleArray + _numTotalSamples) { - // only copy the audio from the sample buffer if there's space - memcpy(_audioSampleArray + _indexOfNextSlot, sampleBuffer, numSamples * sizeof(int16_t)); - _indexOfNextSlot += numSamples; - } -} - -void AudioInjector::clear() { - _indexOfNextSlot = 0; - memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t)); -} - -int16_t& AudioInjector::sampleAt(const int index) { - assert(index >= 0 && index < _numTotalSamples); - - return _audioSampleArray[index]; -} - -void AudioInjector::insertSample(const int index, int sample) { - assert (index >= 0 && index < _numTotalSamples); - - _audioSampleArray[index] = (int16_t) sample; - _indexOfNextSlot = index + 1; -} diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h deleted file mode 100644 index b3a5b82fdf..0000000000 --- a/libraries/audio/src/AudioInjector.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// AudioInjector.h -// hifi -// -// Created by Stephen Birarda on 4/23/13. -// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. -// - -#ifndef __hifi__AudioInjector__ -#define __hifi__AudioInjector__ - -#include -#include - -#include -#include - -#include -#include - -#include "AudioRingBuffer.h" - -const int MAX_INJECTOR_VOLUME = 0xFF; - -const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); - -class AudioInjector : public QObject { - Q_OBJECT - - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) - Q_PROPERTY(uchar volume READ getVolume WRITE setVolume); -public: - AudioInjector(int maxNumSamples); - ~AudioInjector(); - - void injectAudio(UDPSocket* injectorSocket, sockaddr* destinationSocket); - - bool isInjectingAudio() const { return _isInjectingAudio; } - - unsigned char getVolume() const { return _volume; } - void setVolume(unsigned char volume) { _volume = volume; } - - const glm::vec3& getPosition() const { return _position; } - void setPosition(const glm::vec3& position) { _position = position; } - - const glm::quat& getOrientation() const { return _orientation; } - void setOrientation(const glm::quat& orientation) { _orientation = orientation; } - - float getRadius() const { return _radius; } - void setRadius(float radius) { _radius = radius; } - - bool hasSamplesToInject() const { return _indexOfNextSlot > 0; } - - void addSample(const int16_t sample); - void addSamples(int16_t* sampleBuffer, int numSamples); - - void clear(); -public slots: - int16_t& sampleAt(const int index); - void insertSample(const int index, int sample); -private: - QUuid _streamIdentifier; - int16_t* _audioSampleArray; - int _numTotalSamples; - glm::vec3 _position; - glm::quat _orientation; - float _radius; - unsigned char _volume; - int _indexOfNextSlot; - bool _isInjectingAudio; -}; - -#endif /* defined(__hifi__AudioInjector__) */ diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index 18ee037ea9..9adb525b93 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -24,6 +24,8 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier) } +const uchar MAX_INJECTOR_VOLUME = 255; + int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer); diff --git a/libraries/audio/src/InjectedAudioRingBuffer.h b/libraries/audio/src/InjectedAudioRingBuffer.h index bab540919d..b5845366fb 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.h +++ b/libraries/audio/src/InjectedAudioRingBuffer.h @@ -11,8 +11,6 @@ #include -#include "AudioInjector.h" - #include "PositionalAudioRingBuffer.h" class InjectedAudioRingBuffer : public PositionalAudioRingBuffer { diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index cfa43725b8..a38e2dee6d 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -41,8 +41,10 @@ _velocity(0, 0, 0), _controllerButtons(0), _isActive(false), _leapID(LEAPID_INVALID), +_sixenseID(SIXENSEID_INVALID), _numFramesWithoutData(0), -_owningHandData(owningHandData) +_owningHandData(owningHandData), +_isCollidingWithVoxel(false) { for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) { _fingers.push_back(FingerData(this, owningHandData)); diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index b8a06d53ad..8b43b64ef0 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -24,6 +24,7 @@ const int NUM_FINGERS_PER_HAND = 5; const int NUM_FINGERS = NUM_HANDS * NUM_FINGERS_PER_HAND; const int LEAPID_INVALID = -1; +const int SIXENSEID_INVALID = -1; enum RaveGloveEffectsMode { @@ -119,7 +120,7 @@ public: void incrementFramesWithoutData() { _numFramesWithoutData++; } void resetFramesWithoutData() { _numFramesWithoutData = 0; } int getFramesWithoutData() const { return _numFramesWithoutData; } - + private: glm::vec3 _tipRawPosition; glm::vec3 _rootRawPosition; @@ -142,12 +143,16 @@ public: const glm::vec3& getRawNormal() const { return _rawNormal; } bool isActive() const { return _isActive; } int getLeapID() const { return _leapID; } + int getSixenseID() const { return _sixenseID; } + std::vector& getFingers() { return _fingers; } size_t getNumFingers() { return _fingers.size(); } void setActive(bool active) { _isActive = active; } void setLeapID(int id) { _leapID = id; } + void setSixenseID(int id) { _sixenseID = id; } + void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; } void setVelocity(const glm::vec3& velocity) { _velocity = velocity; } @@ -166,6 +171,9 @@ public: void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; } float getJoystickX() { return _joystickX; } float getJoystickY() { return _joystickY; } + + bool getIsCollidingWithVoxel() { return _isCollidingWithVoxel; } + void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; } private: std::vector _fingers; @@ -178,8 +186,12 @@ private: bool _isActive; // This has current valid data int _leapID; // the Leap's serial id for this tracked object + int _sixenseID; // Sixense controller ID for this palm int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost. HandData* _owningHandData; + + bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel + }; #endif /* defined(__hifi__HandData__) */ diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index 223b602dfa..3213e11940 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -48,7 +48,7 @@ bool JurisdictionListener::queueJurisdictionRequest() { NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (nodeList->getNodeActiveSocketOrPing(&(*node)) && node->getType() == NODE_TYPE_VOXEL_SERVER) { - sockaddr* nodeAddress = node->getActiveSocket(); + const HifiSockAddr* nodeAddress = node->getActiveSocket(); PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut); nodeCount++; } @@ -64,9 +64,9 @@ bool JurisdictionListener::queueJurisdictionRequest() { return isStillRunning(); } -void JurisdictionListener::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { +void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { - Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); + Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress); if (node) { QUuid nodeUUID = node->getUUID(); JurisdictionMap map; diff --git a/libraries/octree/src/JurisdictionListener.h b/libraries/octree/src/JurisdictionListener.h index 7376c9196f..a05601169b 100644 --- a/libraries/octree/src/JurisdictionListener.h +++ b/libraries/octree/src/JurisdictionListener.h @@ -45,7 +45,7 @@ protected: /// \param packetData pointer to received data /// \param ssize_t packetLength size of received data /// \thread "this" individual processing thread - virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); + virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength); private: NodeToJurisdictionMap _jurisdictions; diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index 3d3a3bec6f..9bb729f66a 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -29,9 +29,9 @@ JurisdictionSender::~JurisdictionSender() { } -void JurisdictionSender::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { +void JurisdictionSender::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { - Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); + Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress); if (node) { QUuid nodeUUID = node->getUUID(); lockRequestingNodes(); @@ -66,7 +66,7 @@ bool JurisdictionSender::process() { Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID); if (node->getActiveSocket() != NULL) { - sockaddr* nodeAddress = node->getActiveSocket(); + const HifiSockAddr* nodeAddress = node->getActiveSocket(); queuePacketForSending(*nodeAddress, bufferOut, sizeOut); nodeCount++; } diff --git a/libraries/octree/src/JurisdictionSender.h b/libraries/octree/src/JurisdictionSender.h index 1be8128ed0..5f61d41dea 100644 --- a/libraries/octree/src/JurisdictionSender.h +++ b/libraries/octree/src/JurisdictionSender.h @@ -32,7 +32,7 @@ public: virtual bool process(); protected: - virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); + virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength); /// Locks all the resources of the thread. void lockRequestingNodes() { pthread_mutex_lock(&_requestingNodeMutex); } diff --git a/libraries/octree/src/OctreeElementBag.h b/libraries/octree/src/OctreeElementBag.h index 49587d04a0..a952b52095 100644 --- a/libraries/octree/src/OctreeElementBag.h +++ b/libraries/octree/src/OctreeElementBag.h @@ -31,9 +31,6 @@ public: int count() const { return _elementsInUse; } void deleteAll(); - - //static void voxelNodeDeleteHook(OctreeElement* element, void* extraData); - virtual void elementDeleted(OctreeElement* element); private: diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 07e96e4963..59b0bb60d1 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -211,10 +211,6 @@ int Assignment::packToBuffer(unsigned char* buffer) { return numPackedBytes; } -void Assignment::run() { - // run method ovveridden by subclasses -} - QDebug operator<<(QDebug debug, const Assignment &assignment) { debug.nospace() << "UUID: " << assignment.getUUID().toString().toStdString().c_str() << ", Type: " << assignment.getType(); diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index 47aad34838..ec7ae7f74f 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -89,9 +89,6 @@ public: // implement parseData to return 0 so we can be a subclass of NodeData int parseData(unsigned char* sourceBuffer, int numBytes) { return 0; } - /// blocking run of the assignment - virtual void run(); - friend QDebug operator<<(QDebug debug, const Assignment& assignment); friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment); friend QDataStream& operator>>(QDataStream &in, Assignment& assignment); diff --git a/libraries/shared/src/HifiSockAddr.cpp b/libraries/shared/src/HifiSockAddr.cpp new file mode 100644 index 0000000000..0fdc8919ed --- /dev/null +++ b/libraries/shared/src/HifiSockAddr.cpp @@ -0,0 +1,113 @@ +// +// HifiSockAddr.cpp +// hifi +// +// Created by Stephen Birarda on 11/26/2013. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include "HifiSockAddr.h" + +#include +#include + +HifiSockAddr::HifiSockAddr() : + _address(), + _port(0) +{ + +} + +HifiSockAddr::HifiSockAddr(const QHostAddress& address, quint16 port) : + _address(address), + _port(port) +{ + +} + +HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) { + _address = otherSockAddr._address; + _port = otherSockAddr._port; +} + +HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) { + // lookup the IP by the hostname + QHostInfo hostInfo = QHostInfo::fromName(hostname); + foreach(const QHostAddress& address, hostInfo.addresses()) { + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + _address = address; + _port = hostOrderPort; + } + } +} + +HifiSockAddr& HifiSockAddr::operator=(const HifiSockAddr& rhsSockAddr) { + HifiSockAddr temp(rhsSockAddr); + swap(temp); + return *this; +} + +void HifiSockAddr::swap(HifiSockAddr& otherSockAddr) { + using std::swap; + + swap(_address, otherSockAddr._address); + swap(_port, otherSockAddr._port); +} + +int HifiSockAddr::packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr) { + quint32 addressToPack = packSockAddr._address.isNull() ? 0 : packSockAddr._address.toIPv4Address(); + memcpy(packetData, &addressToPack, sizeof(addressToPack)); + memcpy(packetData + sizeof(addressToPack), &packSockAddr._port, sizeof(packSockAddr._port)); + + return sizeof(addressToPack) + sizeof(packSockAddr._port); +} + +int HifiSockAddr::unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr) { + quint32* address = (quint32*) packetData; + unpackDestSockAddr._address = *address == 0 ? QHostAddress() : QHostAddress(*address); + unpackDestSockAddr._port = *((quint16*) (packetData + sizeof(quint32))); + + return sizeof(quint32) + sizeof(quint16); +} + +bool HifiSockAddr::operator==(const HifiSockAddr &rhsSockAddr) const { + return _address == rhsSockAddr._address && _port == rhsSockAddr._port; +} + +QDebug operator<<(QDebug debug, const HifiSockAddr &hifiSockAddr) { + debug.nospace() << hifiSockAddr._address.toString().toLocal8Bit().constData() << ":" << hifiSockAddr._port; + return debug; +} + +quint32 getHostOrderLocalAddress() { + + static int localAddress = 0; + + if (localAddress == 0) { + foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { + if (interface.flags() & QNetworkInterface::IsUp + && interface.flags() & QNetworkInterface::IsRunning + && interface.flags() & ~QNetworkInterface::IsLoopBack) { + // we've decided that this is the active NIC + // enumerate it's addresses to grab the IPv4 address + foreach(const QNetworkAddressEntry &entry, interface.addressEntries()) { + // make sure it's an IPv4 address that isn't the loopback + if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) { + qDebug("Node's local address is %s\n", entry.ip().toString().toLocal8Bit().constData()); + + // set our localAddress and break out + localAddress = entry.ip().toIPv4Address(); + break; + } + } + } + + if (localAddress != 0) { + break; + } + } + } + + // return the looked up local address + return localAddress; +} diff --git a/libraries/shared/src/HifiSockAddr.h b/libraries/shared/src/HifiSockAddr.h new file mode 100644 index 0000000000..f11492805f --- /dev/null +++ b/libraries/shared/src/HifiSockAddr.h @@ -0,0 +1,50 @@ +// +// HifiSockAddr.h +// hifi +// +// Created by Stephen Birarda on 11/26/2013. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__HifiSockAddr__ +#define __hifi__HifiSockAddr__ + +#include + +class HifiSockAddr { +public: + HifiSockAddr(); + HifiSockAddr(const QHostAddress& address, quint16 port); + HifiSockAddr(const HifiSockAddr& otherSockAddr); + HifiSockAddr(const QString& hostname, quint16 hostOrderPort); + + bool isNull() const { return _address.isNull() && _port == 0; } + + HifiSockAddr& operator=(const HifiSockAddr& rhsSockAddr); + void swap(HifiSockAddr& otherSockAddr); + + bool operator==(const HifiSockAddr& rhsSockAddr) const; + bool operator!=(const HifiSockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); } + + const QHostAddress& getAddress() const { return _address; } + QHostAddress* getAddressPointer() { return &_address; } + void setAddress(const QHostAddress& address) { _address = address; } + + quint16 getPort() const { return _port; } + quint16* getPortPointer() { return &_port; } + void setPort(quint16 port) { _port = port; } + + static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr); + static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr); + + friend QDebug operator<<(QDebug debug, const HifiSockAddr &hifiSockAddr); +private: + QHostAddress _address; + quint16 _port; +}; + +quint32 getHostOrderLocalAddress(); + +Q_DECLARE_METATYPE(HifiSockAddr) + +#endif /* defined(__hifi__HifiSockAddr__) */ diff --git a/libraries/shared/src/Logging.cpp b/libraries/shared/src/Logging.cpp index 0182034be6..866d603861 100644 --- a/libraries/shared/src/Logging.cpp +++ b/libraries/shared/src/Logging.cpp @@ -11,35 +11,35 @@ #include #include +#include + +#include "HifiSockAddr.h" #include "SharedUtil.h" #include "NodeList.h" #include "Logging.h" -sockaddr_in Logging::logstashSocket = {}; +HifiSockAddr Logging::logstashSocket = HifiSockAddr(); char* Logging::targetName = NULL; -sockaddr* Logging::socket() { +const HifiSockAddr& Logging::socket() { - if (logstashSocket.sin_addr.s_addr == 0) { + if (logstashSocket.getAddress().isNull()) { // we need to construct the socket object - - // assume IPv4 - logstashSocket.sin_family = AF_INET; - // use the constant port - logstashSocket.sin_port = htons(LOGSTASH_UDP_PORT); + logstashSocket.setPort(htons(LOGSTASH_UDP_PORT)); // lookup the IP address for the constant hostname - struct hostent* logstashHostInfo; - if ((logstashHostInfo = gethostbyname(LOGSTASH_HOSTNAME))) { - memcpy(&logstashSocket.sin_addr, logstashHostInfo->h_addr_list[0], logstashHostInfo->h_length); + QHostInfo hostInfo = QHostInfo::fromName(LOGSTASH_HOSTNAME); + if (!hostInfo.addresses().isEmpty()) { + // use the first IP address + logstashSocket.setAddress(hostInfo.addresses().first()); } else { printf("Failed to lookup logstash IP - will try again on next log attempt.\n"); } } - return (sockaddr*) &logstashSocket; + return logstashSocket; } bool Logging::shouldSendStats() { @@ -57,7 +57,8 @@ void Logging::stashValue(char statType, const char* key, float value) { NodeList *nodeList = NodeList::getInstance(); if (nodeList) { - nodeList->getNodeSocket()->send(socket(), logstashPacket, numPacketBytes); + nodeList->getNodeSocket().writeDatagram(logstashPacket, numPacketBytes, + logstashSocket.getAddress(), logstashSocket.getPort()); } } diff --git a/libraries/shared/src/Logging.h b/libraries/shared/src/Logging.h index c4f921070a..d64c39992b 100644 --- a/libraries/shared/src/Logging.h +++ b/libraries/shared/src/Logging.h @@ -20,11 +20,13 @@ const char STAT_TYPE_TIMER = 't'; const char STAT_TYPE_COUNTER = 'c'; const char STAT_TYPE_GAUGE = 'g'; +class HifiSockAddr; + /// Handles custom message handling and sending of stats/logs to Logstash instance class Logging { public: /// \return the socket used to send stats to logstash - static sockaddr* socket(); + static const HifiSockAddr& socket(); /// checks if this target should send stats to logstash, given its current environment /// \return true if the caller should send stats to logstash @@ -44,7 +46,7 @@ public: /// prints various process, message type, and time information static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message); private: - static sockaddr_in logstashSocket; + static HifiSockAddr logstashSocket; static char* targetName; }; diff --git a/libraries/shared/src/NetworkPacket.cpp b/libraries/shared/src/NetworkPacket.cpp index 0acabbac58..575e80b59b 100644 --- a/libraries/shared/src/NetworkPacket.cpp +++ b/libraries/shared/src/NetworkPacket.cpp @@ -22,10 +22,10 @@ NetworkPacket::~NetworkPacket() { // nothing to do } -void NetworkPacket::copyContents(const sockaddr& address, const unsigned char* packetData, ssize_t packetLength) { +void NetworkPacket::copyContents(const HifiSockAddr& sockAddr, const unsigned char* packetData, ssize_t packetLength) { _packetLength = 0; if (packetLength >=0 && packetLength <= MAX_PACKET_SIZE) { - memcpy(&_address, &address, sizeof(_address)); + _sockAddr = sockAddr; _packetLength = packetLength; memcpy(&_packetData[0], packetData, packetLength); } else { @@ -34,16 +34,16 @@ void NetworkPacket::copyContents(const sockaddr& address, const unsigned char* } NetworkPacket::NetworkPacket(const NetworkPacket& packet) { - copyContents(packet.getAddress(), packet.getData(), packet.getLength()); + copyContents(packet.getSockAddr(), packet.getData(), packet.getLength()); } -NetworkPacket::NetworkPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) { - copyContents(address, packetData, packetLength); +NetworkPacket::NetworkPacket(const HifiSockAddr& sockAddr, unsigned char* packetData, ssize_t packetLength) { + copyContents(sockAddr, packetData, packetLength); }; // copy assignment NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) { - copyContents(other.getAddress(), other.getData(), other.getLength()); + copyContents(other.getSockAddr(), other.getData(), other.getLength()); return *this; } diff --git a/libraries/shared/src/NetworkPacket.h b/libraries/shared/src/NetworkPacket.h index 9c7c61e10f..b30564087d 100644 --- a/libraries/shared/src/NetworkPacket.h +++ b/libraries/shared/src/NetworkPacket.h @@ -15,6 +15,8 @@ #include #include +#include "HifiSockAddr.h" + #include "NodeList.h" // for MAX_PACKET_SIZE /// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet @@ -30,19 +32,19 @@ public: NetworkPacket& operator= (NetworkPacket&& other); // move assignment #endif - NetworkPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength); + NetworkPacket(const HifiSockAddr& sockAddr, unsigned char* packetData, ssize_t packetLength); - sockaddr& getAddress() { return _address; } + const HifiSockAddr& getSockAddr() const { return _sockAddr; } ssize_t getLength() const { return _packetLength; } unsigned char* getData() { return &_packetData[0]; } - const sockaddr& getAddress() const { return _address; } + const HifiSockAddr& getAddress() const { return _sockAddr; } const unsigned char* getData() const { return &_packetData[0]; } private: - void copyContents(const sockaddr& address, const unsigned char* packetData, ssize_t packetLength); + void copyContents(const HifiSockAddr& sockAddr, const unsigned char* packetData, ssize_t packetLength); - sockaddr _address; + HifiSockAddr _sockAddr; ssize_t _packetLength; unsigned char _packetData[MAX_PACKET_SIZE]; }; diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index d4bd8a084a..bcaf64f9c6 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -19,30 +19,25 @@ #include "Node.h" #include "NodeTypes.h" #include "SharedUtil.h" -#include "UDPSocket.h" #include -Node::Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket) : +Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) : _type(type), _uuid(uuid), _wakeMicrostamp(usecTimestampNow()), _lastHeardMicrostamp(usecTimestampNow()), + _publicSocket(publicSocket), + _localSocket(localSocket), _activeSocket(NULL), _bytesReceivedMovingAverage(NULL), _linkedData(NULL), _isAlive(true) { - setPublicSocket(publicSocket); - setLocalSocket(localSocket); - pthread_mutex_init(&_mutex, 0); } Node::~Node() { - delete _publicSocket; - delete _localSocket; - if (_linkedData) { _linkedData->deleteOrDeleteLater(); } @@ -86,47 +81,32 @@ const char* Node::getTypeName() const { } } -void Node::setPublicSocket(sockaddr* publicSocket) { - if (_activeSocket == _publicSocket) { +void Node::setPublicSocket(const HifiSockAddr& publicSocket) { + if (_activeSocket == &_publicSocket) { // if the active socket was the public socket then reset it to NULL _activeSocket = NULL; } - if (publicSocket) { - _publicSocket = new sockaddr(*publicSocket); - } else { - _publicSocket = NULL; - } + _publicSocket = publicSocket; } -void Node::setLocalSocket(sockaddr* localSocket) { - if (_activeSocket == _localSocket) { +void Node::setLocalSocket(const HifiSockAddr& localSocket) { + if (_activeSocket == &_localSocket) { // if the active socket was the local socket then reset it to NULL _activeSocket = NULL; } - if (localSocket) { - _localSocket = new sockaddr(*localSocket); - } else { - _localSocket = NULL; - } + _localSocket = localSocket; } void Node::activateLocalSocket() { qDebug() << "Activating local socket for node" << *this << "\n"; - _activeSocket = _localSocket; + _activeSocket = &_localSocket; } void Node::activatePublicSocket() { qDebug() << "Activating public socket for node" << *this << "\n"; - _activeSocket = _publicSocket; -} - -bool Node::matches(sockaddr* otherPublicSocket, sockaddr* otherLocalSocket, char otherNodeType) { - // checks if two node objects are the same node (same type + local + public address) - return _type == otherNodeType - && socketMatch(_publicSocket, otherPublicSocket) - && socketMatch(_localSocket, otherLocalSocket); + _activeSocket = &_publicSocket; } void Node::recordBytesReceived(int bytesReceived) { @@ -154,15 +134,8 @@ float Node::getAverageKilobitsPerSecond() { } QDebug operator<<(QDebug debug, const Node &node) { - char publicAddressBuffer[16] = {'\0'}; - unsigned short publicAddressPort = loadBufferWithSocketInfo(publicAddressBuffer, node.getPublicSocket()); - - char localAddressBuffer[16] = {'\0'}; - unsigned short localAddressPort = loadBufferWithSocketInfo(localAddressBuffer, node.getLocalSocket()); - debug.nospace() << node.getTypeName() << " (" << node.getType() << ")"; debug << " " << node.getUUID().toString().toLocal8Bit().constData() << " "; - debug.nospace() << publicAddressBuffer << ":" << publicAddressPort; - debug.nospace() << " / " << localAddressBuffer << ":" << localAddressPort; + debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket(); return debug.nospace(); } diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index 3f7780d63d..4614a6f0a8 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -21,19 +21,18 @@ #include #include +#include "HifiSockAddr.h" #include "NodeData.h" #include "SimpleMovingAverage.h" class Node { public: - Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket); + Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); ~Node(); bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } bool operator!=(const Node& otherNode) const { return !(*this == otherNode); } - bool matches(sockaddr* otherPublicSocket, sockaddr* otherLocalSocket, char otherNodeType); - char getType() const { return _type; } void setType(char type) { _type = type; } const char* getTypeName() const; @@ -47,12 +46,12 @@ public: uint64_t getLastHeardMicrostamp() const { return _lastHeardMicrostamp; } void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; } - sockaddr* getPublicSocket() const { return _publicSocket; } - void setPublicSocket(sockaddr* publicSocket); - sockaddr* getLocalSocket() const { return _localSocket; } - void setLocalSocket(sockaddr* localSocket); + const HifiSockAddr& getPublicSocket() const { return _publicSocket; } + void setPublicSocket(const HifiSockAddr& publicSocket); + const HifiSockAddr& getLocalSocket() const { return _localSocket; } + void setLocalSocket(const HifiSockAddr& localSocket); - sockaddr* getActiveSocket() const { return _activeSocket; } + const HifiSockAddr* getActiveSocket() const { return _activeSocket; } void activatePublicSocket(); void activateLocalSocket(); @@ -87,9 +86,9 @@ private: QUuid _uuid; uint64_t _wakeMicrostamp; uint64_t _lastHeardMicrostamp; - sockaddr* _publicSocket; - sockaddr* _localSocket; - sockaddr* _activeSocket; + HifiSockAddr _publicSocket; + HifiSockAddr _localSocket; + HifiSockAddr* _activeSocket; SimpleMovingAverage* _bytesReceivedMovingAverage; NodeData* _linkedData; bool _isAlive; diff --git a/libraries/shared/src/NodeData.h b/libraries/shared/src/NodeData.h index 0a2ba3b514..252b03ccc5 100644 --- a/libraries/shared/src/NodeData.h +++ b/libraries/shared/src/NodeData.h @@ -14,7 +14,7 @@ class Node; class NodeData : public QObject { - Q_OBJECT + Q_OBJECT public: NodeData(Node* owningNode = NULL); diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index daa0d7a4b1..3df90d4377 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -15,6 +15,7 @@ #include #include "Assignment.h" +#include "HifiSockAddr.h" #include "Logging.h" #include "NodeList.h" #include "NodeTypes.h" @@ -61,22 +62,21 @@ NodeList* NodeList::getInstance() { NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : _domainHostname(DEFAULT_DOMAIN_HOSTNAME), - _domainIP(), - _domainPort(DEFAULT_DOMAIN_SERVER_PORT), + _domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _nodeBuckets(), _numNodes(0), - _nodeSocket(newSocketListenPort), + _nodeSocket(), _ownerType(newOwnerType), _nodeTypesOfInterest(NULL), _ownerUUID(QUuid::createUuid()), _numNoReplyDomainCheckIns(0), - _assignmentServerSocket(NULL), - _publicAddress(), - _publicPort(0), + _assignmentServerSocket(), + _publicSockAddr(), _hasCompletedInitialSTUNFailure(false), _stunRequestsSinceSuccess(0) { - + _nodeSocket.bind(QHostAddress::AnyIPv4, newSocketListenPort); + qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort() << "\n"; } NodeList::~NodeList() { @@ -100,29 +100,29 @@ void NodeList::setDomainHostname(const QString& domainHostname) { _domainHostname = domainHostname.left(colonIndex); // grab the port by reading the string after the colon - _domainPort = atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData()); + _domainSockAddr.setPort(atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData())); - qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainPort << "\n"; + qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainSockAddr.getPort() << "\n"; } else { // no port included with the hostname, simply set the member variable and reset the domain server port to default _domainHostname = domainHostname; - _domainPort = DEFAULT_DOMAIN_SERVER_PORT; + _domainSockAddr.setPort(DEFAULT_DOMAIN_SERVER_PORT); } // clear the NodeList so nodes from this domain are killed clear(); // reset our _domainIP to the null address so that a lookup happens on next check in - _domainIP.clear(); + _domainSockAddr.setAddress(QHostAddress::Null); notifyDomainChanged(); } } -void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { +void NodeList::timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData) { for(NodeList::iterator node = begin(); node != end(); node++) { - if (socketMatch(node->getPublicSocket(), nodeAddress) || - socketMatch(node->getLocalSocket(), nodeAddress)) { + if (node->getPublicSocket() == nodeAddress || + node->getLocalSocket() == nodeAddress) { int pingTime = usecTimestampNow() - *(uint64_t*)(packetData + numBytesForPacketHeader(packetData)); @@ -132,11 +132,11 @@ void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { } } -void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetData, size_t dataBytes) { +void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, unsigned char* packetData, size_t dataBytes) { switch (packetData[0]) { case PACKET_TYPE_DOMAIN: { // only process the DS if this is our current domain server - if (_domainIP == QHostAddress(senderAddress)) { + if (_domainSockAddr == senderSockAddr) { processDomainServerList(packetData, dataBytes); } @@ -145,15 +145,15 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat case PACKET_TYPE_PING: { // send it right back populateTypeAndVersion(packetData, PACKET_TYPE_PING_REPLY); - _nodeSocket.send(senderAddress, packetData, dataBytes); + _nodeSocket.writeDatagram((char*) packetData, dataBytes, senderSockAddr.getAddress(), senderSockAddr.getPort()); break; } case PACKET_TYPE_PING_REPLY: { // activate the appropriate socket for this node, if not yet updated - activateSocketFromNodeCommunication(senderAddress); + activateSocketFromNodeCommunication(senderSockAddr); // set the ping time for this node for stat collection - timePingReply(senderAddress, packetData); + timePingReply(senderSockAddr, packetData); break; } case PACKET_TYPE_STUN_RESPONSE: { @@ -169,7 +169,7 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat } } -void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes) { +void NodeList::processBulkNodeData(const HifiSockAddr& senderAddress, unsigned char *packetData, int numTotalBytes) { // find the avatar mixer in our node list and update the lastRecvTime from it Node* bulkSendNode = nodeWithAddress(senderAddress); @@ -198,11 +198,11 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe if (!matchingNode) { // we're missing this node, we need to add it to the list - matchingNode = addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, NULL, NULL); + matchingNode = addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, HifiSockAddr(), HifiSockAddr()); } currentPosition += updateNodeWithData(matchingNode, - NULL, + HifiSockAddr(), packetHolder, numTotalBytes - (currentPosition - startPosition)); @@ -210,16 +210,16 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe } } -int NodeList::updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes) { +int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes) { node->lock(); node->setLastHeardMicrostamp(usecTimestampNow()); - if (senderAddress) { - activateSocketFromNodeCommunication(senderAddress); + if (!senderSockAddr.isNull()) { + activateSocketFromNodeCommunication(senderSockAddr); } - if (node->getActiveSocket() || !senderAddress) { + if (node->getActiveSocket() || senderSockAddr.isNull()) { node->recordBytesReceived(dataBytes); if (!node->getLinkedData() && linkedDataCreateCallback) { @@ -238,9 +238,9 @@ int NodeList::updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned c } } -Node* NodeList::nodeWithAddress(sockaddr *senderAddress) { +Node* NodeList::nodeWithAddress(const HifiSockAddr &senderSockAddr) { for(NodeList::iterator node = begin(); node != end(); node++) { - if (node->getActiveSocket() && socketMatch(node->getActiveSocket(), senderAddress)) { + if (node->getActiveSocket() && *node->getActiveSocket() == senderSockAddr) { return &(*node); } } @@ -340,25 +340,15 @@ void NodeList::sendSTUNRequest() { memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID)); // lookup the IP for the STUN server - static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME); + static HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); - for (int i = 0; i < stunInfo.addresses().size(); i++) { - if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { - QString stunIPAddress = stunInfo.addresses()[i].toString(); - - if (!_hasCompletedInitialSTUNFailure) { - qDebug("Sending intial stun request to %s\n", stunIPAddress.toLocal8Bit().constData()); - } - - _nodeSocket.send(stunIPAddress.toLocal8Bit().constData(), - STUN_SERVER_PORT, - stunRequestPacket, - sizeof(stunRequestPacket)); - - break; - } + if (!_hasCompletedInitialSTUNFailure) { + qDebug("Sending intial stun request to %s\n", stunSockAddr.getAddress().toString().toLocal8Bit().constData()); } + _nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket), + stunSockAddr.getAddress(), stunSockAddr.getPort()); + _stunRequestsSinceSuccess++; if (_stunRequestsSinceSuccess >= NUM_STUN_REQUESTS_BEFORE_FALLBACK) { @@ -372,8 +362,8 @@ void NodeList::sendSTUNRequest() { } // reset the public address and port - _publicAddress = QHostAddress::Null; - _publicPort = 0; + // use 0 so the DS knows to act as out STUN server + _publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort()); } } @@ -425,13 +415,12 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes) QHostAddress newPublicAddress = QHostAddress(stunAddress); - if (newPublicAddress != _publicAddress || newPublicPort != _publicPort) { - _publicAddress = newPublicAddress; - _publicPort = newPublicPort; + if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) { + _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); qDebug("New public socket received from STUN server is %s:%hu\n", - _publicAddress.toString().toLocal8Bit().constData(), - _publicPort); + _publicSockAddr.getAddress().toString().toLocal8Bit().constData(), + _publicSockAddr.getPort()); } @@ -489,17 +478,17 @@ void NodeList::sendDomainServerCheckIn() { static bool printedDomainServerIP = false; // Lookup the IP address of the domain server if we need to - if (_domainIP.isNull()) { + if (_domainSockAddr.getAddress().isNull()) { qDebug("Looking up DS hostname %s.\n", _domainHostname.toLocal8Bit().constData()); QHostInfo domainServerHostInfo = QHostInfo::fromName(_domainHostname); for (int i = 0; i < domainServerHostInfo.addresses().size(); i++) { if (domainServerHostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { - _domainIP = domainServerHostInfo.addresses()[i]; + _domainSockAddr.setAddress(domainServerHostInfo.addresses()[i]); qDebug("DS at %s is at %s\n", _domainHostname.toLocal8Bit().constData(), - _domainIP.toString().toLocal8Bit().constData()); + _domainSockAddr.getAddress().toString().toLocal8Bit().constData()); printedDomainServerIP = true; @@ -512,11 +501,11 @@ void NodeList::sendDomainServerCheckIn() { } } } else if (!printedDomainServerIP) { - qDebug("Domain Server IP: %s\n", _domainIP.toString().toLocal8Bit().constData()); + qDebug("Domain Server IP: %s\n", _domainSockAddr.getAddress().toString().toLocal8Bit().constData()); printedDomainServerIP = true; } - if (_publicAddress.isNull() && !_hasCompletedInitialSTUNFailure) { + if (_publicSockAddr.isNull() && !_hasCompletedInitialSTUNFailure) { // we don't know our public socket and we need to send it to the domain server // send a STUN request to figure it out sendSTUNRequest(); @@ -548,13 +537,12 @@ void NodeList::sendDomainServerCheckIn() { packetPosition += rfcOwnerUUID.size(); // pack our public address to send to domain-server - packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket), - htonl(_publicAddress.toIPv4Address()), htons(_publicPort)); + packetPosition += HifiSockAddr::packSockAddr(checkInPacket + (packetPosition - checkInPacket), _publicSockAddr); // pack our local address to send to domain-server - packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket), - getLocalAddress(), - htons(_nodeSocket.getListeningPort())); + packetPosition += HifiSockAddr::packSockAddr(checkInPacket + (packetPosition - checkInPacket), + HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), + _nodeSocket.localPort())); // add the number of bytes for node types of interest *(packetPosition++) = numBytesNodesOfInterest; @@ -567,9 +555,8 @@ void NodeList::sendDomainServerCheckIn() { packetPosition += numBytesNodesOfInterest; } - _nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, checkInPacket, - packetPosition - checkInPacket); - + _nodeSocket.writeDatagram((char*) checkInPacket, packetPosition - checkInPacket, + _domainSockAddr.getAddress(), _domainSockAddr.getPort()); const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; @@ -592,10 +579,8 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte char nodeType; // assumes only IPv4 addresses - sockaddr_in nodePublicSocket; - nodePublicSocket.sin_family = AF_INET; - sockaddr_in nodeLocalSocket; - nodeLocalSocket.sin_family = AF_INET; + HifiSockAddr nodePublicSocket; + HifiSockAddr nodeLocalSocket; unsigned char* readPtr = packetData + numBytesForPacketHeader(packetData); unsigned char* startPtr = packetData; @@ -605,24 +590,22 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) readPtr, NUM_BYTES_RFC4122_UUID)); readPtr += NUM_BYTES_RFC4122_UUID; - readPtr += unpackSocket(readPtr, (sockaddr*) &nodePublicSocket); - readPtr += unpackSocket(readPtr, (sockaddr*) &nodeLocalSocket); + readPtr += HifiSockAddr::unpackSockAddr(readPtr, nodePublicSocket); + readPtr += HifiSockAddr::unpackSockAddr(readPtr, nodeLocalSocket); // if the public socket address is 0 then it's reachable at the same IP // as the domain server - if (nodePublicSocket.sin_addr.s_addr == 0) { - nodePublicSocket.sin_addr.s_addr = htonl(_domainIP.toIPv4Address()); + if (nodePublicSocket.getAddress().isNull()) { + nodePublicSocket.setAddress(_domainSockAddr.getAddress()); } - addOrUpdateNode(nodeUUID, nodeType, (sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket); + addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket); } return readNodes; } -const sockaddr_in DEFAULT_LOCAL_ASSIGNMENT_SOCKET = socketForHostnameAndHostOrderPort(LOCAL_ASSIGNMENT_SERVER_HOSTNAME, - DEFAULT_DOMAIN_SERVER_PORT); void NodeList::sendAssignment(Assignment& assignment) { unsigned char assignmentPacket[MAX_PACKET_SIZE]; @@ -633,14 +616,18 @@ void NodeList::sendAssignment(Assignment& assignment) { int numHeaderBytes = populateTypeAndVersion(assignmentPacket, assignmentPacketType); int numAssignmentBytes = assignment.packToBuffer(assignmentPacket + numHeaderBytes); - sockaddr* assignmentServerSocket = (_assignmentServerSocket == NULL) - ? (sockaddr*) &DEFAULT_LOCAL_ASSIGNMENT_SOCKET - : _assignmentServerSocket; + static HifiSockAddr DEFAULT_ASSIGNMENT_SOCKET(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, DEFAULT_DOMAIN_SERVER_PORT); - _nodeSocket.send(assignmentServerSocket, assignmentPacket, numHeaderBytes + numAssignmentBytes); + const HifiSockAddr* assignmentServerSocket = _assignmentServerSocket.isNull() + ? &DEFAULT_ASSIGNMENT_SOCKET + : &_assignmentServerSocket; + + _nodeSocket.writeDatagram((char*) assignmentPacket, numHeaderBytes + numAssignmentBytes, + assignmentServerSocket->getAddress(), + assignmentServerSocket->getPort()); } -void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) const { +void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) { uint64_t currentTime = 0; @@ -652,11 +639,14 @@ void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) const { memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime)); // send the ping packet to the local and public sockets for this node - _nodeSocket.send(node->getLocalSocket(), pingPacket, sizeof(pingPacket)); - _nodeSocket.send(node->getPublicSocket(), pingPacket, sizeof(pingPacket)); + _nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket), + node->getLocalSocket().getAddress(), node->getLocalSocket().getPort()); + _nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket), + node->getPublicSocket().getAddress(), node->getPublicSocket().getPort()); } -Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket) { +Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, + const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) { NodeList::iterator node = end(); for (node = begin(); node != end(); node++) { @@ -684,12 +674,12 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publ } // check if we need to change this node's public or local sockets - if (!socketMatch(publicSocket, node->getPublicSocket())) { + if (publicSocket != node->getPublicSocket()) { node->setPublicSocket(publicSocket); qDebug() << "Public socket change for node" << *node << "\n"; } - if (!socketMatch(localSocket, node->getLocalSocket())) { + if (localSocket != node->getLocalSocket()) { node->setLocalSocket(localSocket); qDebug() << "Local socket change for node" << *node << "\n"; } @@ -725,7 +715,8 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt if (memchr(nodeTypes, node->getType(), numNodeTypes)) { if (getNodeActiveSocketOrPing(&(*node))) { // we know which socket is good for this node, send there - _nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes); + _nodeSocket.writeDatagram((char*) broadcastData, dataBytes, + node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort()); ++n; } } @@ -733,25 +724,16 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt return n; } -const uint64_t PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000; - -void NodeList::possiblyPingInactiveNodes() { - static timeval lastPing = {}; - - // make sure PING_INACTIVE_NODE_INTERVAL_USECS has elapsed since last ping - if (usecTimestampNow() - usecTimestamp(&lastPing) >= PING_INACTIVE_NODE_INTERVAL_USECS) { - gettimeofday(&lastPing, NULL); - - for(NodeList::iterator node = begin(); node != end(); node++) { - if (!node->getActiveSocket()) { - // we don't have an active link to this node, ping it to set that up - pingPublicAndLocalSocketsForInactiveNode(&(*node)); - } +void NodeList::pingInactiveNodes() { + for(NodeList::iterator node = begin(); node != end(); node++) { + if (!node->getActiveSocket()) { + // we don't have an active link to this node, ping it to set that up + pingPublicAndLocalSocketsForInactiveNode(&(*node)); } } } -sockaddr* NodeList::getNodeActiveSocketOrPing(Node* node) { +const HifiSockAddr* NodeList::getNodeActiveSocketOrPing(Node* node) { if (node->getActiveSocket()) { return node->getActiveSocket(); } else { @@ -760,15 +742,15 @@ sockaddr* NodeList::getNodeActiveSocketOrPing(Node* node) { } } -void NodeList::activateSocketFromNodeCommunication(sockaddr *nodeAddress) { +void NodeList::activateSocketFromNodeCommunication(const HifiSockAddr& nodeAddress) { for(NodeList::iterator node = begin(); node != end(); node++) { if (!node->getActiveSocket()) { // check both the public and local addresses for each node to see if we find a match // prioritize the private address so that we prune erroneous local matches - if (socketMatch(node->getPublicSocket(), nodeAddress)) { + if (node->getPublicSocket() == nodeAddress) { node->activatePublicSocket(); break; - } else if (socketMatch(node->getLocalSocket(), nodeAddress)) { + } else if (node->getLocalSocket() == nodeAddress) { node->activateLocalSocket(); break; } @@ -804,7 +786,22 @@ void NodeList::killNode(Node* node, bool mustLockNode) { } } -void* removeSilentNodes(void *args) { +void NodeList::removeSilentNodes() { + NodeList* nodeList = NodeList::getInstance(); + + for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) { + node->lock(); + + if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) { + // kill this node, don't lock - we already did it + nodeList->killNode(&(*node), false); + } + + node->unlock(); + } +} + +void* removeSilentNodesAndSleep(void *args) { NodeList* nodeList = (NodeList*) args; uint64_t checkTimeUsecs = 0; int sleepTime = 0; @@ -813,16 +810,7 @@ void* removeSilentNodes(void *args) { checkTimeUsecs = usecTimestampNow(); - for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) { - node->lock(); - - if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) { - // kill this node, don't lock - we already did it - nodeList->killNode(&(*node), false); - } - - node->unlock(); - } + nodeList->removeSilentNodes(); sleepTime = NODE_SILENCE_THRESHOLD_USECS - (usecTimestampNow() - checkTimeUsecs); @@ -845,7 +833,7 @@ void* removeSilentNodes(void *args) { void NodeList::startSilentNodeRemovalThread() { if (!::silentNodeThreadStopFlag) { - pthread_create(&removeSilentNodesThread, NULL, removeSilentNodes, (void*) this); + pthread_create(&removeSilentNodesThread, NULL, removeSilentNodesAndSleep, (void*) this); } else { qDebug("Refusing to start silent node removal thread from previously failed join.\n"); } @@ -969,7 +957,7 @@ void NodeListIterator::skipDeadAndStopIncrement() { void NodeList::addDomainListener(DomainChangeListener* listener) { _domainListeners.push_back(listener); - QString domain = _domainHostname.isEmpty() ? _domainIP.toString() : _domainHostname; + QString domain = _domainHostname.isEmpty() ? _domainSockAddr.getAddress().toString() : _domainHostname; listener->domainChanged(domain); } diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 1e7b181cb0..250474ee75 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -15,11 +15,11 @@ #include #include +#include #include #include "Node.h" #include "NodeTypes.h" -#include "UDPSocket.h" #ifdef _WIN32 #include "pthread.h" @@ -31,7 +31,8 @@ const int NODES_PER_BUCKET = 100; const int MAX_PACKET_SIZE = 1500; const uint64_t NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000; -const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000; +const uint64_t DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000; +const uint64_t PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000; extern const char SOLO_NODE_TYPES[2]; @@ -40,11 +41,12 @@ const int MAX_HOSTNAME_BYTES = 256; extern const QString DEFAULT_DOMAIN_HOSTNAME; extern const unsigned short DEFAULT_DOMAIN_SERVER_PORT; -const char LOCAL_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost"; +const char DEFAULT_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost"; const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5; class Assignment; +class HifiSockAddr; class NodeListIterator; // Callers who want to hook add/kill callbacks should implement this class @@ -59,7 +61,8 @@ public: virtual void domainChanged(QString domain) = 0; }; -class NodeList { +class NodeList : public QObject { + Q_OBJECT public: static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = 0); static NodeList* getInstance(); @@ -75,19 +78,17 @@ public: const QString& getDomainHostname() const { return _domainHostname; } void setDomainHostname(const QString& domainHostname); - const QHostAddress& getDomainIP() const { return _domainIP; } - void setDomainIP(const QHostAddress& domainIP) { _domainIP = domainIP; } - void setDomainIPToLocalhost() { _domainIP = QHostAddress(INADDR_LOOPBACK); } + const QHostAddress& getDomainIP() const { return _domainSockAddr.getAddress(); } + void setDomainIPToLocalhost() { _domainSockAddr.setAddress(QHostAddress(INADDR_LOOPBACK)); } - unsigned short getDomainPort() const { return _domainPort; } - void setDomainPort(unsigned short domainPort) { _domainPort = domainPort; } + void setDomainSockAddr(const HifiSockAddr& domainSockAddr) { _domainSockAddr = domainSockAddr; } + + unsigned short getDomainPort() const { return _domainSockAddr.getPort(); } const QUuid& getOwnerUUID() const { return _ownerUUID; } void setOwnerUUID(const QUuid& ownerUUID) { _ownerUUID = ownerUUID; } - UDPSocket* getNodeSocket() { return &_nodeSocket; } - - unsigned short int getSocketListenPort() const { return _nodeSocket.getListeningPort(); } + QUdpSocket& getNodeSocket() { return _nodeSocket; } void(*linkedDataCreateCallback)(Node *); @@ -101,26 +102,25 @@ public: void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest); - void sendDomainServerCheckIn(); int processDomainServerList(unsigned char *packetData, size_t dataBytes); - void setAssignmentServerSocket(sockaddr* serverSocket) { _assignmentServerSocket = serverSocket; } + void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); - void pingPublicAndLocalSocketsForInactiveNode(Node* node) const; + void pingPublicAndLocalSocketsForInactiveNode(Node* node); void sendKillNode(const char* nodeTypes, int numNodeTypes); - Node* nodeWithAddress(sockaddr *senderAddress); + Node* nodeWithAddress(const HifiSockAddr& senderSockAddr); Node* nodeWithUUID(const QUuid& nodeUUID); - Node* addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket); + Node* addOrUpdateNode(const QUuid& uuid, char nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); void killNode(Node* node, bool mustLockNode = true); - void processNodeData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes); - void processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes); + void processNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, size_t dataBytes); + void processBulkNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, int numTotalBytes); - int updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes); + int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes); unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes); @@ -142,8 +142,11 @@ public: void addDomainListener(DomainChangeListener* listener); void removeDomainListener(DomainChangeListener* listener); - void possiblyPingInactiveNodes(); - sockaddr* getNodeActiveSocketOrPing(Node* node); + const HifiSockAddr* getNodeActiveSocketOrPing(Node* node); +public slots: + void sendDomainServerCheckIn(); + void pingInactiveNodes(); + void removeSilentNodes(); private: static NodeList* _sharedInstance; @@ -160,25 +163,23 @@ private: void processKillNode(unsigned char* packetData, size_t dataBytes); QString _domainHostname; - QHostAddress _domainIP; - unsigned short _domainPort; + HifiSockAddr _domainSockAddr; Node** _nodeBuckets[MAX_NUM_NODES / NODES_PER_BUCKET]; int _numNodes; - UDPSocket _nodeSocket; + QUdpSocket _nodeSocket; char _ownerType; char* _nodeTypesOfInterest; QUuid _ownerUUID; pthread_t removeSilentNodesThread; pthread_t checkInWithDomainServerThread; int _numNoReplyDomainCheckIns; - sockaddr* _assignmentServerSocket; - QHostAddress _publicAddress; - uint16_t _publicPort; + HifiSockAddr _assignmentServerSocket; + HifiSockAddr _publicSockAddr; bool _hasCompletedInitialSTUNFailure; unsigned int _stunRequestsSinceSuccess; - void activateSocketFromNodeCommunication(sockaddr *nodeAddress); - void timePingReply(sockaddr *nodeAddress, unsigned char *packetData); + void activateSocketFromNodeCommunication(const HifiSockAddr& nodeSockAddr); + void timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData); std::vector _hooks; std::vector _domainListeners; diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 027a6a4d68..5ac27efd40 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -63,6 +63,9 @@ int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsi int parentSections = numberOfThreeBitSectionsInCode(ancestorOctalCode); int branchStartBit = parentSections * 3; + // Note: this does not appear to be "multi-byte length code" safe. When octal codes are larger than 255 bytes + // long, the length code is stored in two bytes. The "1" below appears to assume that the length is always one + // byte long. return sectionValue(descendantOctalCode + 1 + (branchStartBit / 8), branchStartBit % 8); } diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 5d5f812760..6ca20141d4 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -34,7 +34,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_DOMAIN: case PACKET_TYPE_DOMAIN_LIST_REQUEST: case PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY: - return 1; + return 2; case PACKET_TYPE_VOXEL_QUERY: return 2; diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index 3c0b0c80d8..f829a80ac0 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -44,7 +44,7 @@ PacketSender::PacketSender(PacketSenderNotify* notify, int packetsPerSecond) : } -void PacketSender::queuePacketForSending(sockaddr& address, unsigned char* packetData, ssize_t packetLength) { +void PacketSender::queuePacketForSending(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) { NetworkPacket packet(address, packetData, packetLength); lock(); _packets.push_back(packet); @@ -334,9 +334,9 @@ bool PacketSender::nonThreadedProcess() { unlock(); // send the packet through the NodeList... - UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket(); - - nodeSocket->send(&temporary.getAddress(), temporary.getData(), temporary.getLength()); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) temporary.getData(), temporary.getLength(), + temporary.getSockAddr().getAddress(), + temporary.getSockAddr().getPort()); packetsSentThisCall++; _packetsOverCheckInterval++; _totalPacketsSent++; diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h index 1731f70cbf..b05e42d11b 100644 --- a/libraries/shared/src/PacketSender.h +++ b/libraries/shared/src/PacketSender.h @@ -38,11 +38,11 @@ public: PacketSender(PacketSenderNotify* notify = NULL, int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND); /// Add packet to outbound queue. - /// \param sockaddr& address the destination address + /// \param HifiSockAddr& address the destination address /// \param packetData pointer to data /// \param ssize_t packetLength size of data /// \thread any thread, typically the application thread - void queuePacketForSending(sockaddr& address, unsigned char* packetData, ssize_t packetLength); + void queuePacketForSending(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength); void setPacketsPerSecond(int packetsPerSecond) { _packetsPerSecond = std::max(MINIMUM_PACKETS_PER_SECOND, packetsPerSecond); } diff --git a/libraries/shared/src/ReceivedPacketProcessor.cpp b/libraries/shared/src/ReceivedPacketProcessor.cpp index a46cd36dbe..4ad869460e 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.cpp +++ b/libraries/shared/src/ReceivedPacketProcessor.cpp @@ -16,9 +16,9 @@ ReceivedPacketProcessor::ReceivedPacketProcessor() { _dontSleep = false; } -void ReceivedPacketProcessor::queueReceivedPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) { +void ReceivedPacketProcessor::queueReceivedPacket(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) { // Make sure our Node and NodeList knows we've heard from this node. - Node* node = NodeList::getInstance()->nodeWithAddress(&address); + Node* node = NodeList::getInstance()->nodeWithAddress(address); if (node) { node->setLastHeardMicrostamp(usecTimestampNow()); } @@ -44,7 +44,7 @@ bool ReceivedPacketProcessor::process() { NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us _packets.erase(_packets.begin()); // remove the oldest packet unlock(); // let others add to the packets - processPacket(temporary.getAddress(), temporary.getData(), temporary.getLength()); // process our temporary copy + processPacket(temporary.getSockAddr(), temporary.getData(), temporary.getLength()); // process our temporary copy } return isStillRunning(); // keep running till they terminate us } diff --git a/libraries/shared/src/ReceivedPacketProcessor.h b/libraries/shared/src/ReceivedPacketProcessor.h index f36473ae12..621204025e 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.h +++ b/libraries/shared/src/ReceivedPacketProcessor.h @@ -24,7 +24,7 @@ public: /// \param packetData pointer to received data /// \param ssize_t packetLength size of received data /// \thread network receive thread - void queueReceivedPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); + void queueReceivedPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength); /// Are there received packets waiting to be processed bool hasPacketsToProcess() const { return _packets.size() > 0; } @@ -38,7 +38,7 @@ protected: /// \param packetData pointer to received data /// \param ssize_t packetLength size of received data /// \thread "this" individual processing thread - virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) = 0; + virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) = 0; /// Implements generic processing behavior for this thread. virtual bool process(); diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp new file mode 100644 index 0000000000..095999adca --- /dev/null +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -0,0 +1,32 @@ +// +// ThreadedAssignment.cpp +// hifi +// +// Created by Stephen Birarda on 12/3/2013. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include "ThreadedAssignment.h" + +ThreadedAssignment::ThreadedAssignment(const unsigned char* dataBuffer, int numBytes) : + Assignment(dataBuffer, numBytes), + _isFinished(false) +{ + +} + +void ThreadedAssignment::setFinished(bool isFinished) { + _isFinished = isFinished; + + if (_isFinished) { + emit finished(); + } +} + +void ThreadedAssignment::checkInWithDomainServerOrExit() { + if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + setFinished(true); + } else { + NodeList::getInstance()->sendDomainServerCheckIn(); + } +} diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h new file mode 100644 index 0000000000..2d6e49b724 --- /dev/null +++ b/libraries/shared/src/ThreadedAssignment.h @@ -0,0 +1,34 @@ +// +// ThreadedAssignment.h +// hifi +// +// Created by Stephen Birarda on 12/3/2013. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__ThreadedAssignment__ +#define __hifi__ThreadedAssignment__ + +#include "Assignment.h" + +class ThreadedAssignment : public Assignment { + Q_OBJECT +public: + ThreadedAssignment(const unsigned char* dataBuffer, int numBytes); + + void setFinished(bool isFinished); +public slots: + /// threaded run of assignment + virtual void run() = 0; + + virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) = 0; +protected: + bool _isFinished; +private slots: + void checkInWithDomainServerOrExit(); +signals: + void finished(); +}; + + +#endif /* defined(__hifi__ThreadedAssignment__) */ diff --git a/libraries/shared/src/UDPSocket.cpp b/libraries/shared/src/UDPSocket.cpp deleted file mode 100644 index 1318fbc4a5..0000000000 --- a/libraries/shared/src/UDPSocket.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// -// UDPSocket.cpp -// interface -// -// Created by Stephen Birarda on 1/28/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include "Syssocket.h" -#else -#include -#include -#include -#include -#endif - -#include -#include -#include - -#include "Logging.h" -#include "UDPSocket.h" - -sockaddr_in destSockaddr, senderAddress; - -bool socketMatch(const sockaddr* first, const sockaddr* second) { - if (first != NULL && second != NULL) { - // utility function that indicates if two sockets are equivalent - - // currently only compares two IPv4 addresses - // expandable to IPv6 by adding else if for AF_INET6 - - if (first->sa_family != second->sa_family) { - // not the same family, can't be equal - return false; - } else if (first->sa_family == AF_INET) { - const sockaddr_in *firstIn = (const sockaddr_in *) first; - const sockaddr_in *secondIn = (const sockaddr_in *) second; - - return firstIn->sin_addr.s_addr == secondIn->sin_addr.s_addr - && firstIn->sin_port == secondIn->sin_port; - } else { - return false; - } - } else { - return false; - } -} - -int packSocket(unsigned char* packStore, in_addr_t inAddress, in_port_t networkOrderPort) { - packStore[0] = inAddress >> 24; - packStore[1] = inAddress >> 16; - packStore[2] = inAddress >> 8; - packStore[3] = inAddress; - - packStore[4] = networkOrderPort >> 8; - packStore[5] = networkOrderPort; - - return 6; // could be dynamically more if we need IPv6 -} - -int packSocket(unsigned char* packStore, sockaddr* socketToPack) { - return packSocket(packStore, ((sockaddr_in*) socketToPack)->sin_addr.s_addr, ((sockaddr_in*) socketToPack)->sin_port); -} - -int unpackSocket(const unsigned char* packedData, sockaddr* unpackDestSocket) { - sockaddr_in* destinationSocket = (sockaddr_in*) unpackDestSocket; - destinationSocket->sin_family = AF_INET; - destinationSocket->sin_addr.s_addr = (packedData[0] << 24) + (packedData[1] << 16) + (packedData[2] << 8) + packedData[3]; - destinationSocket->sin_port = (packedData[4] << 8) + packedData[5]; - return 6; // this could be more if we ever need IPv6 -} - -void copySocketToEmptySocketPointer(sockaddr** destination, const sockaddr* source) { - // create a new sockaddr or sockaddr_in depending on what type of address this is - if (source->sa_family == AF_INET) { - *destination = (sockaddr*) new sockaddr_in; - memcpy(*destination, source, sizeof(sockaddr_in)); - } else { - *destination = (sockaddr*) new sockaddr_in6; - memcpy(*destination, source, sizeof(sockaddr_in6)); - } -} - -int getLocalAddress() { - - static int localAddress = 0; - - if (localAddress == 0) { - foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { - if (interface.flags() & QNetworkInterface::IsUp - && interface.flags() & QNetworkInterface::IsRunning - && interface.flags() & ~QNetworkInterface::IsLoopBack) { - // we've decided that this is the active NIC - // enumerate it's addresses to grab the IPv4 address - foreach(const QNetworkAddressEntry &entry, interface.addressEntries()) { - // make sure it's an IPv4 address that isn't the loopback - if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) { - qDebug("Node's local address is %s\n", entry.ip().toString().toLocal8Bit().constData()); - - // set our localAddress and break out - localAddress = htonl(entry.ip().toIPv4Address()); - break; - } - } - } - - if (localAddress != 0) { - break; - } - } - } - - // return the looked up local address - return localAddress; -} - -unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket) { - if (socket != NULL) { - char* copyBuffer = inet_ntoa(((sockaddr_in*) socket)->sin_addr); - memcpy(addressBuffer, copyBuffer, strlen(copyBuffer)); - return htons(((sockaddr_in*) socket)->sin_port); - } else { - const char* unknownAddress = "Unknown"; - memcpy(addressBuffer, unknownAddress, strlen(unknownAddress)); - return 0; - } -} - -sockaddr_in socketForHostnameAndHostOrderPort(const char* hostname, unsigned short port) { - struct hostent* pHostInfo; - sockaddr_in newSocket = {}; - - if ((pHostInfo = gethostbyname(hostname))) { - memcpy(&newSocket.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length); - } - - if (port != 0) { - newSocket.sin_port = htons(port); - } - - return newSocket; -} - -UDPSocket::UDPSocket(unsigned short int listeningPort) : - _listeningPort(listeningPort), - blocking(true) -{ - init(); - // create the socket - handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - if (handle <= 0) { - qDebug("Failed to create socket.\n"); - return; - } - - destSockaddr.sin_family = AF_INET; - - // bind the socket to the passed listeningPort - sockaddr_in bind_address; - bind_address.sin_family = AF_INET; - bind_address.sin_addr.s_addr = INADDR_ANY; - bind_address.sin_port = htons((uint16_t) _listeningPort); - - if (bind(handle, (const sockaddr*) &bind_address, sizeof(sockaddr_in)) < 0) { - qDebug("Failed to bind socket to port %hu.\n", _listeningPort); - return; - } - - // if we requested an ephemeral port, get the actual port - if (listeningPort == 0) { - socklen_t addressLength = sizeof(sockaddr_in); - getsockname(handle, (sockaddr*) &bind_address, &addressLength); - _listeningPort = ntohs(bind_address.sin_port); - } - - const int DEFAULT_BLOCKING_SOCKET_TIMEOUT_USECS = 0.5 * 1000000; - setBlockingReceiveTimeoutInUsecs(DEFAULT_BLOCKING_SOCKET_TIMEOUT_USECS); - - qDebug("Created UDP Socket listening on %hd\n", _listeningPort); -} - -UDPSocket::~UDPSocket() { -#ifdef _WIN32 - closesocket(handle); -#else - close(handle); -#endif -} - -bool UDPSocket::init() { -#ifdef _WIN32 - WORD wVersionRequested; - WSADATA wsaData; - int err; - - wVersionRequested = MAKEWORD( 2, 2 ); - - err = WSAStartup( wVersionRequested, &wsaData ); - if ( err != 0 ) { - /* Tell the user that we could not find a usable */ - /* WinSock DLL. */ - return false; - } - - /* Confirm that the WinSock DLL supports 2.2.*/ - /* Note that if the DLL supports versions later */ - /* than 2.2 in addition to 2.2, it will still return */ - /* 2.2 in wVersion since that is the version we */ - /* requested. */ - - if ( LOBYTE( wsaData.wVersion ) != 2 || - HIBYTE( wsaData.wVersion ) != 2 ) { - /* Tell the user that we could not find a usable */ - /* WinSock DLL. */ - WSACleanup(); - return false; - } -#endif - return true; -} - -void UDPSocket::setBlocking(bool blocking) { - this->blocking = blocking; - -#ifdef _WIN32 - u_long mode = blocking ? 0 : 1; - ioctlsocket(handle, FIONBIO, &mode); -#else - int flags = fcntl(handle, F_GETFL, 0); - fcntl(handle, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)); -#endif -} - -void UDPSocket::setBlockingReceiveTimeoutInUsecs(int timeoutUsecs) { - struct timeval tv = {timeoutUsecs / 1000000, timeoutUsecs % 1000000}; - setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); -} - -// Receive data on this socket with retrieving address of sender -bool UDPSocket::receive(void* receivedData, ssize_t* receivedBytes) const { - return receive((sockaddr*) &senderAddress, receivedData, receivedBytes); -} - -// Receive data on this socket with the address of the sender -bool UDPSocket::receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const { - -#ifdef _WIN32 - int addressSize = sizeof(*recvAddress); -#else - socklen_t addressSize = sizeof(*recvAddress); -#endif - *receivedBytes = recvfrom(handle, static_cast(receivedData), MAX_BUFFER_LENGTH_BYTES, - 0, recvAddress, &addressSize); - - return (*receivedBytes > 0); -} - -int UDPSocket::send(sockaddr* destAddress, const void* data, size_t byteLength) const { - if (destAddress) { - // send data via UDP - int sent_bytes = sendto(handle, (const char*)data, byteLength, - 0, (sockaddr *) destAddress, sizeof(sockaddr_in)); - - if (sent_bytes != byteLength) { - qDebug("Failed to send packet: %s\n", strerror(errno)); - return false; - } - - return sent_bytes; - } else { - qDebug("UDPSocket send called with NULL destination address - Likely a node with no active socket.\n"); - return 0; - } - -} - -int UDPSocket::send(const char* destAddress, int destPort, const void* data, size_t byteLength) const { - - // change address and port on reusable global to passed variables - destSockaddr.sin_addr.s_addr = inet_addr(destAddress); - destSockaddr.sin_port = htons((uint16_t)destPort); - - return send((sockaddr *)&destSockaddr, data, byteLength); -} diff --git a/libraries/shared/src/UDPSocket.h b/libraries/shared/src/UDPSocket.h deleted file mode 100644 index b177fd4e30..0000000000 --- a/libraries/shared/src/UDPSocket.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// UDPSocket.h -// interface -// -// Created by Stephen Birarda on 1/28/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__UDPSocket__ -#define __interface__UDPSocket__ - -#ifdef _WIN32 -#include "Syssocket.h" -#else -#include -#include -#endif - -#define MAX_BUFFER_LENGTH_BYTES 1500 - -class UDPSocket { -public: - UDPSocket(unsigned short int listeningPort); - ~UDPSocket(); - - bool init(); - unsigned short int getListeningPort() const { return _listeningPort; } - - void setBlocking(bool blocking); - bool isBlocking() const { return blocking; } - void setBlockingReceiveTimeoutInUsecs(int timeoutUsecs); - - int send(sockaddr* destAddress, const void* data, size_t byteLength) const; - int send(const char* destAddress, int destPort, const void* data, size_t byteLength) const; - - bool receive(void* receivedData, ssize_t* receivedBytes) const; - bool receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const; -private: - int handle; - unsigned short int _listeningPort; - bool blocking; -}; - -bool socketMatch(const sockaddr* first, const sockaddr* second); -int packSocket(unsigned char* packStore, in_addr_t inAddress, in_port_t networkOrderPort); -int packSocket(unsigned char* packStore, sockaddr* socketToPack); -int unpackSocket(const unsigned char* packedData, sockaddr* unpackDestSocket); -void copySocketToEmptySocketPointer(sockaddr** destination, const sockaddr* source); -int getLocalAddress(); -unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket); -sockaddr_in socketForHostnameAndHostOrderPort(const char* hostname, unsigned short port = 0); - -#endif /* defined(__interface__UDPSocket__) */ diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 01f6e9ae76..669979e0b0 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -145,11 +145,15 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& } // actually send it - NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + 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()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + 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. @@ -169,8 +173,9 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& truePacketsSent++; packetsSent++; - NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); packetSent = true; @@ -191,8 +196,9 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& // If there's actually a packet waiting, then send it. if (nodeData->isPacketWaiting()) { // just send the voxel packet - NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); packetSent = true; int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); @@ -518,7 +524,9 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength); } - NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), _tempOutputBuffer, envPacketLength); + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); trueBytesSent += envPacketLength; truePacketsSent++; packetsSentThisInterval++; diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 13cb8515be..96323f7d1a 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -53,8 +54,10 @@ void attachVoxelNodeDataToNode(Node* newNode) { VoxelServer* VoxelServer::_theInstance = NULL; -VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes), - _serverTree(true) { +VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : + ThreadedAssignment(dataBuffer, numBytes), + _serverTree(true) +{ _argc = 0; _argv = NULL; @@ -495,6 +498,69 @@ void VoxelServer::parsePayload() { } } +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() { @@ -647,12 +713,7 @@ void VoxelServer::run() { qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval); } - sockaddr senderAddress; - - unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; - ssize_t packetLength; - - timeval lastDomainServerCheckIn = {}; + HifiSockAddr senderSockAddr; // set up our jurisdiction broadcaster... _jurisdictionSender = new JurisdictionSender(_jurisdiction); @@ -679,82 +740,21 @@ void VoxelServer::run() { } 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 (true) { - // check for >= in case one gets past the goalie - if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { - qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n"; - break; - } - - // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed - if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { - gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(); - } - - // ping our inactive nodes to punch holes with them - nodeList->possiblyPingInactiveNodes(); - - if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) && - packetVersionMatch(packetData)) { - - int numBytesPacketHeader = numBytesForPacketHeader(packetData); - - if (packetData[0] == PACKET_TYPE_VOXEL_QUERY) { - bool debug = false; - if (debug) { - qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow()); - } - - // 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(QByteArray((char*)packetData + numBytesPacketHeader, - NUM_BYTES_RFC4122_UUID)); - - Node* node = nodeList->nodeWithUUID(nodeUUID); - - if (node) { - nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength); - 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 (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { - if (_jurisdictionSender) { - _jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength); - } - } else if (_voxelServerPacketProcessor && - (packetData[0] == PACKET_TYPE_SET_VOXEL - || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE - || packetData[0] == PACKET_TYPE_ERASE_VOXEL)) { - - - const char* messageName; - switch (packetData[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(senderAddress, packetData, packetLength); - } else { - // let processNodeData handle it. - NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); - } - } + while (!_isFinished) { + QCoreApplication::processEvents(); } // call NodeList::clear() so that all of our node specific objects, including our sending threads, are diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index 4aee48e5f1..f4923778c1 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include "civetweb.h" @@ -26,14 +26,13 @@ #include "VoxelServerPacketProcessor.h" /// Handles assignments of type VoxelServer - sending voxels to various clients. -class VoxelServer : public Assignment { +class VoxelServer : public ThreadedAssignment { public: VoxelServer(const unsigned char* dataBuffer, int numBytes); ~VoxelServer(); - /// runs the voxel server assignment - void run(); + /// allows setting of run arguments void setArguments(int argc, char** argv); @@ -59,7 +58,11 @@ public: bool isInitialLoadComplete() const { return (_voxelPersistThread) ? _voxelPersistThread->isInitialLoadComplete() : true; } time_t* getLoadCompleted() { return (_voxelPersistThread) ? _voxelPersistThread->getLoadCompleted() : NULL; } uint64_t getLoadElapsedTime() const { return (_voxelPersistThread) ? _voxelPersistThread->getLoadElapsedTime() : 0; } +public slots: + /// runs the voxel server assignment + void run(); + void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); private: int _argc; const char** _argv; diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp index f69fabd06f..3a0bc70f42 100644 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp @@ -39,7 +39,8 @@ void VoxelServerPacketProcessor::resetStats() { } -void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { +void VoxelServerPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, + unsigned char* packetData, ssize_t packetLength) { bool debugProcessPacket = _myServer->wantsVerboseDebug(); @@ -135,7 +136,7 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned } // Make sure our Node and NodeList knows we've heard from this node. - Node* senderNode = NodeList::getInstance()->nodeWithAddress(&senderAddress); + Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); QUuid& nodeUUID = DEFAULT_NODE_ID_REF; if (senderNode) { senderNode->setLastHeardMicrostamp(usecTimestampNow()); @@ -170,7 +171,7 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned _myServer->getServerTree().unlock(); // Make sure our Node and NodeList knows we've heard from this node. - Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); + Node* node = NodeList::getInstance()->nodeWithAddress(senderSockAddr); if (node) { node->setLastHeardMicrostamp(usecTimestampNow()); } diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h index 2408e0dd60..4abdbad284 100644 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h @@ -63,7 +63,7 @@ public: NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; } protected: - virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); + virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength); private: void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime, diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index ec7ea3810b..bfc42da63f 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -123,7 +123,7 @@ void VoxelEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned ch if (node->getType() == NODE_TYPE_VOXEL_SERVER && ((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) { if (nodeList->getNodeActiveSocketOrPing(&(*node))) { - sockaddr* nodeAddress = node->getActiveSocket(); + const HifiSockAddr* nodeAddress = node->getActiveSocket(); queuePacketForSending(*nodeAddress, buffer, length); // debugging output... diff --git a/pairing-server/CMakeLists.txt b/pairing-server/CMakeLists.txt index 5e0adcb378..b1fbdf82dc 100644 --- a/pairing-server/CMakeLists.txt +++ b/pairing-server/CMakeLists.txt @@ -5,9 +5,13 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) set(TARGET_NAME pairing-server) +find_package(Qt5Network REQUIRED) + include(${MACRO_DIR}/SetupHifiProject.cmake) setup_hifi_project(${TARGET_NAME} TRUE) +qt5_use_modules(${TARGET_NAME} Network) + # link the shared hifi library include(${MACRO_DIR}/LinkHifiLibrary.cmake) link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file diff --git a/pairing-server/src/main.cpp b/pairing-server/src/main.cpp index 0422251403..609f9412e9 100644 --- a/pairing-server/src/main.cpp +++ b/pairing-server/src/main.cpp @@ -11,17 +11,20 @@ #include #include -#include #include +#include + +#include + const int PAIRING_SERVER_LISTEN_PORT = 7247; const int MAX_PACKET_SIZE_BYTES = 1400; struct PairableDevice { char identifier[64]; char name[64]; - sockaddr_in sendingSocket; - sockaddr_in localSocket; + HifiSockAddr sendingSocket; + HifiSockAddr localSocket; }; struct RequestingClient { @@ -29,7 +32,7 @@ struct RequestingClient { int port; }; -UDPSocket serverSocket(PAIRING_SERVER_LISTEN_PORT); +QUdpSocket serverSocket; PairableDevice* lastDevice = NULL; RequestingClient* lastClient = NULL; @@ -47,17 +50,21 @@ void sendLastClientToLastDevice() { char pairData[INET_ADDRSTRLEN + 6] = {}; int bytesWritten = sprintf(pairData, "%s:%d", ::lastClient->address, ::lastClient->port); - ::serverSocket.send((sockaddr*) &::lastDevice->sendingSocket, pairData, bytesWritten); + ::serverSocket.writeDatagram(pairData, bytesWritten, + ::lastDevice->sendingSocket.getAddress(), ::lastDevice->sendingSocket.getPort()); } int main(int argc, const char* argv[]) { - sockaddr_in senderSocket; + serverSocket.bind(QHostAddress::LocalHost, PAIRING_SERVER_LISTEN_PORT); + + HifiSockAddr senderSockAddr; char senderData[MAX_PACKET_SIZE_BYTES] = {}; - ssize_t receivedBytes = 0; while (true) { - if (::serverSocket.receive((sockaddr*) &senderSocket, &senderData, &receivedBytes)) { + if (::serverSocket.hasPendingDatagrams() + && ::serverSocket.readDatagram(senderData, MAX_PACKET_SIZE_BYTES, + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer())) { if (senderData[0] == 'A') { // this is a device reporting itself as available @@ -76,12 +83,11 @@ int main(int argc, const char* argv[]) { // if we have fewer than 3 matches the packet wasn't properly formatted // setup the localSocket for the pairing device - tempDevice.localSocket.sin_family = AF_INET; - inet_pton(AF_INET, deviceAddress, &::lastDevice); - tempDevice.localSocket.sin_port = socketPort; + tempDevice.localSocket.setAddress(QHostAddress(deviceAddress)); + tempDevice.localSocket.setPort(socketPort); // store this device's sending socket so we can talk back to it - tempDevice.sendingSocket = senderSocket; + tempDevice.sendingSocket = senderSockAddr; // push this new device into the vector printf("New last device is %s (%s) at %s:%d\n", diff --git a/space-server/CMakeLists.txt b/space-server/CMakeLists.txt deleted file mode 100644 index c821992378..0000000000 --- a/space-server/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -set(ROOT_DIR ..) -set(MACRO_DIR ${ROOT_DIR}/cmake/macros) - -set(TARGET_NAME space-server) - -include(${MACRO_DIR}/SetupHifiProject.cmake) - -setup_hifi_project(${TARGET_NAME} TRUE) - -include(${MACRO_DIR}/LinkHifiLibrary.cmake) -link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file diff --git a/space-server/example.data.txt b/space-server/example.data.txt deleted file mode 100644 index 065017c308..0000000000 --- a/space-server/example.data.txt +++ /dev/null @@ -1,2 +0,0 @@ -0 00000100001011101110 domain4.highfidelity.co domain4 -0 00000100110100010001 domain3.highfidelity.co domain3 diff --git a/space-server/src/TreeNode.cpp b/space-server/src/TreeNode.cpp deleted file mode 100644 index 7326b2db0d..0000000000 --- a/space-server/src/TreeNode.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// -// TreeNode.cpp -// hifi -// -// Created by Stephen Birarda on 2/13/13. -// -// - -#include "TreeNode.h" - -std::string EMPTY_STRING = ""; - -TreeNode::TreeNode() { - for (int i = 0; i < CHILDREN_PER_NODE; ++i) { - child[i] = NULL; - } - - hostname = NULL; - nickname = NULL; -} \ No newline at end of file diff --git a/space-server/src/TreeNode.h b/space-server/src/TreeNode.h deleted file mode 100644 index ad766d953f..0000000000 --- a/space-server/src/TreeNode.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// TreeNode.h -// hifi -// -// Created by Stephen Birarda on 2/13/13. -// -// - -#ifndef __hifi__TreeNode__ -#define __hifi__TreeNode__ - -#include - -const int CHILDREN_PER_NODE = 8; - -class TreeNode { -public: - TreeNode(); - - TreeNode *child[CHILDREN_PER_NODE]; - char *hostname; - char *nickname; - int domain_id; -}; - -#endif /* defined(__hifi__TreeNode__) */ diff --git a/space-server/src/main.cpp b/space-server/src/main.cpp deleted file mode 100644 index 24d73da9c1..0000000000 --- a/space-server/src/main.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// -// main.cpp -// space -// -// Created by Leonardo Murillo on 2/6/13. -// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. -// - -#include -#include -#include -#include - -#include "TreeNode.h" -#include "UDPSocket.h" - -const char *CONFIG_FILE = "/Users/birarda/code/worklist/checkouts/hifi/space/example.data.txt"; -const unsigned short SPACE_LISTENING_PORT = 55551; -const short MAX_NAME_LENGTH = 255; - -const char ROOT_HOSTNAME[] = "root.highfidelity.co"; -const char ROOT_NICKNAME[] = "root"; - -const size_t PACKET_LENGTH_BYTES = 1024; - -sockaddr_in destAddress; -socklen_t destLength = sizeof(destAddress); - -char *lastKnownHostname; - -TreeNode rootNode; -UDPSocket spaceSocket(SPACE_LISTENING_PORT); - -TreeNode *findOrCreateNode(int lengthInBits, - unsigned char *addressBytes, - char *hostname, - char *nickname, - int domainID) { - - TreeNode *currentNode = &rootNode; - - for (int i = 0; i < lengthInBits; i += 3) { - unsigned char octet; - - if (i%8 < 6) { - octet = addressBytes[i/8] << i%8 >> (5); - } else { - octet = (addressBytes[i/8] << i >> (11 - i)) | (addressBytes[i/8 + 1] >> (11 - i + 2)); - } - - if (currentNode->child[octet] == NULL) { - currentNode->child[octet] = new TreeNode; - } else if (currentNode->child[octet]->hostname != NULL) { - lastKnownHostname = currentNode->child[octet]->hostname; - } - - currentNode = currentNode->child[octet]; - } - - if (currentNode->hostname == NULL) { - currentNode->hostname = hostname; - currentNode->nickname = nickname; - } - - return currentNode; -}; - -bool loadSpaceData(void) { - FILE *configFile = std::fopen(CONFIG_FILE, "r"); - char formatString[10]; - - if (configFile == NULL) { - std::cout << "Unable to load config file!\n"; - return false; - } else { - char *lengthBitString = new char[8]; - int itemsRead = 0; - - while ((itemsRead = fscanf(configFile, "0 %8c", lengthBitString)) > 0) { - - // calculate the number of bits in the address and bits required for padding - unsigned long threeBitCodes = strtoul(lengthBitString, NULL, 2); - int bitsInAddress = threeBitCodes * 3; - int paddingBits = 8 - (bitsInAddress % 8); - int addressByteLength = (bitsInAddress + paddingBits) / 8; - - // create an unsigned char * to hold the padded address - unsigned char *paddedAddress = new unsigned char[addressByteLength]; - - char *fullByteBitString = new char[8]; - - for (int c = 0; c < addressByteLength; c++) { - if (c + 1 == addressByteLength && paddingBits > 0) { - // this is the last byte, and we need some padding bits - // pull as many bits as are left - int goodBits = 8 - paddingBits; - sprintf(formatString, "%%%dc", goodBits); - itemsRead = fscanf(configFile, formatString, fullByteBitString); - - // fill out the rest with zeros - memset(fullByteBitString + goodBits, '0', paddingBits); - } else { - // pull 8 bits (which will be one byte) from the file - itemsRead = fscanf(configFile, "%8c", fullByteBitString); - } - - // set the corresponding value in the unsigned char array - *(paddedAddress + c) = strtoul(fullByteBitString, NULL, 2); - } - - char *nodeHostname = new char[MAX_NAME_LENGTH]; - char *nodeNickname = new char[MAX_NAME_LENGTH]; - itemsRead = fscanf(configFile, "%s %s\n", nodeHostname, nodeNickname); - - findOrCreateNode(bitsInAddress, paddedAddress, nodeHostname, nodeNickname, 0); - } - - std::fclose(configFile); - - return true; - } -} - -int main (int argc, const char *argv[]) { - - setvbuf(stdout, NULL, _IOLBF, 0); - - unsigned char packetData[PACKET_LENGTH_BYTES]; - ssize_t receivedBytes = 0; - - rootNode.hostname = new char[MAX_NAME_LENGTH]; - rootNode.nickname = new char[MAX_NAME_LENGTH]; - - strcpy(rootNode.hostname, ROOT_HOSTNAME); - strcpy(rootNode.nickname, ROOT_NICKNAME); - - loadSpaceData(); - - std::cout << "[DEBUG] Listening for Datagrams" << std::endl; - - while (true) { - if (spaceSocket.receive((sockaddr *)&destAddress, &packetData, &receivedBytes)) { - unsigned long lengthInBits; - lengthInBits = packetData[0] * 3; - - unsigned char addressData[sizeof(packetData)-1]; - for (int i = 0; i < sizeof(packetData)-1; ++i) { - addressData[i] = packetData[i+1]; - } - - TreeNode *thisNode = findOrCreateNode(lengthInBits, addressData, NULL, NULL, 0); - char *hostnameToSend = (thisNode->hostname == NULL) - ? lastKnownHostname - : thisNode->hostname; - - spaceSocket.send((sockaddr *)&destAddress, &hostnameToSend, sizeof(hostnameToSend)); - } - } -} -