diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 9376d43f9d..199d6721da 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -74,7 +74,7 @@ void DomainServer::civetwebUploadHandler(struct mg_connection *connection, const } void DomainServer::nodeAdded(Node* node) { - + NodeList::getInstance()->increaseNodeID(); } void DomainServer::nodeKilled(Node* node) { @@ -98,7 +98,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())), _staticAssignmentFileData(NULL), _voxelServerConfig(NULL) -{ +{ DomainServer::setDomainServerInstance(this); const char CUSTOM_PORT_OPTION[] = "-p"; @@ -117,6 +117,83 @@ DomainServer::DomainServer(int argc, char* argv[]) : const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig"; _voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION); + + // setup the mongoose web server + struct mg_callbacks callbacks = {}; + + QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath()); + + // list of options. Last element must be NULL. + const char* options[] = {"listening_ports", "8080", + "document_root", documentRoot.toLocal8Bit().constData(), NULL}; + + callbacks.begin_request = civetwebRequestHandler; + callbacks.upload = civetwebUploadHandler; + + // Start the web server. + mg_start(&callbacks, NULL, options); +} + +void DomainServer::prepopulateStaticAssignmentFile() { + const uint NUM_FRESH_STATIC_ASSIGNMENTS = 3; + + // write a fresh static assignment array to file + + std::array freshStaticAssignments; + + // pre-populate the first static assignment list with assignments for root AuM, AvM, VS + freshStaticAssignments[0] = Assignment(Assignment::CreateCommand, + Assignment::AudioMixerType, + Assignment::LocalLocation); + freshStaticAssignments[1] = Assignment(Assignment::CreateCommand, + Assignment::AvatarMixerType, + Assignment::LocalLocation); + + Assignment voxelServerAssignment(Assignment::CreateCommand, Assignment::VoxelServerType, Assignment::LocalLocation); + + // Handle Domain/Voxel Server configuration command line arguments + if (_voxelServerConfig) { + qDebug("Reading Voxel Server Configuration.\n"); + qDebug() << " config: " << _voxelServerConfig << "\n"; + int payloadLength = strlen(_voxelServerConfig) + sizeof(char); + voxelServerAssignment.setPayload((const uchar*)_voxelServerConfig, payloadLength); + } + + freshStaticAssignments[2] = voxelServerAssignment; + + _staticAssignmentFile.open(QIODevice::WriteOnly); + + _staticAssignmentFile.write((char*) &NUM_FRESH_STATIC_ASSIGNMENTS, + sizeof(uint16_t)); + _staticAssignmentFile.write((char*) &freshStaticAssignments, sizeof(freshStaticAssignments)); + _staticAssignmentFile.resize(MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS * sizeof(Assignment)); + _staticAssignmentFile.close(); +} + +int DomainServer::checkInMatchesStaticAssignment(NODE_TYPE nodeType, const uchar* checkInData) { + // pull the UUID passed with the check in + QUuid checkInUUID = QUuid::fromRfc4122(QByteArray((const char*) checkInData + numBytesForPacketHeader(checkInData) + + sizeof(NODE_TYPE), + NUM_BYTES_RFC4122_UUID)); + int staticAssignmentIndex = 0; + + while (staticAssignmentIndex < _staticFileAssignments->size() - 1 + && !(*_staticFileAssignments)[staticAssignmentIndex].getUUID().isNull()) { + Assignment* staticAssignment = &(*_staticFileAssignments)[staticAssignmentIndex]; + + if (staticAssignment->getType() == Assignment::typeForNodeType(nodeType) + && staticAssignment->getUUID() == checkInUUID) { + // return index of match + return staticAssignmentIndex; + } else { + // no match, keep looking + staticAssignmentIndex++; + } + } + + // return -1 for no match + return -1; + } Assignment* DomainServer::deployableAssignmentForRequest(Assignment& requestAssignment) { @@ -172,42 +249,6 @@ void DomainServer::cleanup() { _staticAssignmentFile.close(); } -void DomainServer::prepopulateStaticAssignmentFile() { - const uint NUM_FRESH_STATIC_ASSIGNMENTS = 3; - - // write a fresh static assignment array to file - - std::array freshStaticAssignments; - - // pre-populate the first static assignment list with assignments for root AuM, AvM, VS - freshStaticAssignments[0] = Assignment(Assignment::CreateCommand, - Assignment::AudioMixerType, - Assignment::LocalLocation); - freshStaticAssignments[1] = Assignment(Assignment::CreateCommand, - Assignment::AvatarMixerType, - Assignment::LocalLocation); - - Assignment voxelServerAssignment(Assignment::CreateCommand, Assignment::VoxelServerType, Assignment::LocalLocation); - - // Handle Domain/Voxel Server configuration command line arguments - if (_voxelServerConfig) { - qDebug("Reading Voxel Server Configuration.\n"); - qDebug() << " config: " << _voxelServerConfig << "\n"; - int payloadLength = strlen(_voxelServerConfig) + sizeof(char); - voxelServerAssignment.setPayload((const uchar*)_voxelServerConfig, payloadLength); - } - - freshStaticAssignments[2] = voxelServerAssignment; - - _staticAssignmentFile.open(QIODevice::WriteOnly); - - _staticAssignmentFile.write((char*) &NUM_FRESH_STATIC_ASSIGNMENTS, - sizeof(uint16_t)); - _staticAssignmentFile.write((char*) &freshStaticAssignments, sizeof(freshStaticAssignments)); - _staticAssignmentFile.resize(MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS * sizeof(Assignment)); - _staticAssignmentFile.close(); -} - int DomainServer::run() { NodeList* nodeList = NodeList::getInstance(); @@ -241,34 +282,11 @@ int DomainServer::run() { _staticFileAssignments = (std::array*) (_staticAssignmentFileData + sizeof(*_numAssignmentsInStaticFile)); - // construct a local socket to send with our created assignments to the global AS - sockaddr_in localSocket = {}; - localSocket.sin_family = AF_INET; - localSocket.sin_port = htons(nodeList->getInstance()->getNodeSocket()->getListeningPort()); - localSocket.sin_addr.s_addr = serverLocalAddress; - - // setup the mongoose web server - struct mg_context* ctx; - struct mg_callbacks callbacks = {}; - - QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath()); - - // list of options. Last element must be NULL. - const char* options[] = {"listening_ports", "8080", - "document_root", documentRoot.toLocal8Bit().constData(), NULL}; - - callbacks.begin_request = civetwebRequestHandler; - callbacks.upload = civetwebUploadHandler; - - // Start the web server. - ctx = mg_start(&callbacks, NULL, options); - while (true) { while (nodeList->getNodeSocket()->receive((sockaddr *)&nodePublicAddress, packetData, &receivedBytes) && 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 - std::map newestSoloNodes; int numBytesSenderHeader = numBytesForPacketHeader(packetData); @@ -287,104 +305,61 @@ int DomainServer::run() { nodePublicAddress.sin_addr.s_addr = 0; } - bool matchedUUID = true; + const char STATICALLY_ASSIGNED_NODES[3] = { + NODE_TYPE_AUDIO_MIXER, + NODE_TYPE_AVATAR_MIXER, + NODE_TYPE_VOXEL_SERVER + }; - if ((nodeType == NODE_TYPE_AVATAR_MIXER || nodeType == NODE_TYPE_AUDIO_MIXER) && - !nodeList->soloNodeOfType(nodeType)) { - // if this is an audio-mixer or an avatar-mixer and we don't have one yet - // we need to check the GUID of the assignment in the queue - // (if it exists) to make sure there is a match - - // reset matchedUUID to false so there is no match by default - matchedUUID = false; - - // pull the UUID passed with the check in - QUuid checkInUUID = QUuid::fromRfc4122(QByteArray((const char*) packetData + numBytesSenderHeader + - sizeof(NODE_TYPE), - NUM_BYTES_RFC4122_UUID)); - - // lock the assignment queue - _assignmentQueueMutex.lock(); - - std::deque::iterator assignment = _assignmentQueue.begin(); - - Assignment::Type matchType = nodeType == NODE_TYPE_AUDIO_MIXER - ? Assignment::AudioMixerType : Assignment::AvatarMixerType; - - // enumerate the assignments and see if there is a type and UUID match - while (assignment != _assignmentQueue.end()) { - - if ((*assignment)->getType() == matchType - && (*assignment)->getUUID() == checkInUUID) { - // type and UUID match - matchedUUID = true; - - // remove this assignment from the queue - _assignmentQueue.erase(assignment); - - break; - } else { - // no match, keep looking - assignment++; - } - } - - // unlock the assignment queue - _assignmentQueueMutex.unlock(); - } + int matchingStaticAssignmentIndex = -1; - if (matchedUUID) { - Node* newNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress, + if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL || + (matchingStaticAssignmentIndex = checkInMatchesStaticAssignment(nodeType, packetData)) != -1) { + + Node* checkInNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType, nodeList->getLastNodeID()); - // if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it - if (newNode) { - if (newNode->getNodeID() == nodeList->getLastNodeID()) { - nodeList->increaseNodeID(); - } - - int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN); - - currentBufferPos = broadcastPacket + numHeaderBytes; - startPointer = currentBufferPos; - - int numBytesUUID = (nodeType == NODE_TYPE_AUDIO_MIXER || nodeType == NODE_TYPE_AVATAR_MIXER) - ? NUM_BYTES_RFC4122_UUID - : 0; - - unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + numBytesUUID + - sizeof(NODE_TYPE) + numBytesSocket + sizeof(unsigned char); - int numInterestTypes = *(nodeTypesOfInterest - 1); - - 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) && - memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) { - - // don't send avatar nodes to other avatars, that will come from avatar mixer - if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) { - currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node)); - } - + int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN); + + currentBufferPos = broadcastPacket + numHeaderBytes; + startPointer = currentBufferPos; + + int numBytesUUID = (nodeType == NODE_TYPE_AUDIO_MIXER || nodeType == NODE_TYPE_AVATAR_MIXER) + ? NUM_BYTES_RFC4122_UUID + : 0; + + unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + numBytesUUID + + sizeof(NODE_TYPE) + numBytesSocket + sizeof(unsigned char); + int numInterestTypes = *(nodeTypesOfInterest - 1); + + 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) && + memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) { + + // don't send avatar nodes to other avatars, that will come from avatar mixer + if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) { + currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node)); } + } } - - // update last receive to now - uint64_t timeNow = usecTimestampNow(); - newNode->setLastHeardMicrostamp(timeNow); - - // add the node ID to the end of the pointer - currentBufferPos += packNodeId(currentBufferPos, newNode->getNodeID()); - - // send the constructed list back to this node - nodeList->getNodeSocket()->send((sockaddr*)&replyDestinationSocket, - broadcastPacket, - (currentBufferPos - startPointer) + numHeaderBytes); } + + // update last receive to now + uint64_t timeNow = usecTimestampNow(); + checkInNode->setLastHeardMicrostamp(timeNow); + + // add the node ID to the end of the pointer + currentBufferPos += packNodeId(currentBufferPos, checkInNode->getNodeID()); + + // send the constructed list back to this node + nodeList->getNodeSocket()->send((sockaddr*)&replyDestinationSocket, + broadcastPacket, + (currentBufferPos - startPointer) + numHeaderBytes); } } else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) { @@ -421,8 +396,12 @@ int DomainServer::run() { _assignmentQueue.push_back(createAssignment); _assignmentQueueMutex.unlock(); - // also add this assignment to the static map of assignments so it exists next time the DS starts up - (*_staticFileAssignments)[_staticFileAssignments->size()] = *createAssignment; + // find the first available spot in the static assignments and put this assignment there + for (int i = 0; i < _staticFileAssignments->size() - 1; i++) { + if ((*_staticFileAssignments)[i].getUUID().isNull()) { + (*_staticFileAssignments)[i] = *createAssignment; + } + } } } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 46b9ea077d..9f5ba3fdac 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -42,13 +42,15 @@ private: static DomainServer* domainServerInstance; - void cleanup(); void prepopulateStaticAssignmentFile(); + int checkInMatchesStaticAssignment(NODE_TYPE nodeType, const uchar* checkInUUID); + Assignment* deployableAssignmentForRequest(Assignment& requestAssignment); + + void cleanup(); + unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd); - Assignment* deployableAssignmentForRequest(Assignment& requestAssignment); - QMutex _assignmentQueueMutex; std::deque _assignmentQueue; diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 400f128db9..8aa678873d 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -16,6 +16,21 @@ const char IPv4_ADDRESS_DESIGNATOR = 4; const char IPv6_ADDRESS_DESIGNATOR = 6; +Assignment::Type Assignment::typeForNodeType(NODE_TYPE nodeType) { + switch (nodeType) { + case NODE_TYPE_AUDIO_MIXER: + return Assignment::AudioMixerType; + case NODE_TYPE_AVATAR_MIXER: + return Assignment::AvatarMixerType; + case NODE_TYPE_AGENT: + return Assignment::AgentType; + case NODE_TYPE_VOXEL_SERVER: + return Assignment::VoxelServerType; + default: + return Assignment::AllTypes; + } +} + Assignment::Assignment() : _uuid(), _command(Assignment::RequestCommand), diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index eca5baff5d..817710468c 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -42,6 +42,8 @@ public: LocalLocation }; + static Assignment::Type typeForNodeType(NODE_TYPE nodeType); + Assignment(); Assignment(Assignment::Command command, Assignment::Type type,