diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 1315b0ce84..39d1eecca6 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -23,4 +23,5 @@ include_glm(${TARGET_NAME} ${ROOT_DIR}) include(${MACRO_DIR}/LinkHifiLibrary.cmake) link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) -link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file +link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 16522a2d87..63943c97b5 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -22,6 +22,7 @@ #include #include #include +#include const long long ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000; const char PARENT_TARGET_NAME[] = "assignment-client-monitor"; @@ -97,6 +98,8 @@ void childClient() { AudioMixer::run(); } else if (deployedAssignment.getType() == Assignment::AvatarMixerType) { AvatarMixer::run(); + } else if (deployedAssignment.getType() == Assignment::VoxelServerType) { + VoxelServer::run(); } else { // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index 7129e01585..044ea0b369 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -145,6 +145,10 @@ int main(int argc, const char* argv[]) { Assignment avatarMixerAssignment(Assignment::CreateCommand, Assignment::AvatarMixerType, Assignment::LocalLocation); + + Assignment voxelServerAssignment(Assignment::CreateCommand, + Assignment::VoxelServerType, + Assignment::LocalLocation); // construct a local socket to send with our created assignments to the global AS sockaddr_in localSocket = {}; @@ -168,6 +172,9 @@ int main(int argc, const char* argv[]) { // Start the web server. ctx = mg_start(&callbacks, NULL, options); + // wait to check on voxel-servers till we've given our NodeList a chance to get a good list + int checkForVoxelServerAttempt = 0; + while (true) { ::assignmentQueueMutex.lock(); @@ -184,6 +191,27 @@ int main(int argc, const char* argv[]) { qDebug("Missing an audio mixer and assignment not in queue. Adding.\n"); ::assignmentQueue.push_front(&audioMixerAssignment); } + + // Now handle voxel servers. Couple of things are special about voxel servers. + // 1) They can run standalone, and so we want to wait to ask for an assignment until we've given them sufficient + // time to check in with us. So we will look for them, but we want actually add assignments unless we haven't + // seen one after a few tries. + // 2) They aren't soloNodeOfType() so we have to count them. + int voxelServerCount = 0; + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + voxelServerCount++; + } + } + const int MIN_VOXEL_SERVER_CHECKS = 10; + if (checkForVoxelServerAttempt > MIN_VOXEL_SERVER_CHECKS && + voxelServerCount == 0 && + std::find(::assignmentQueue.begin(), ::assignmentQueue.end(), &voxelServerAssignment) == ::assignmentQueue.end()) { + qDebug("Missing a Voxel Server and assignment not in queue. Adding.\n"); + ::assignmentQueue.push_front(&voxelServerAssignment); + } + checkForVoxelServerAttempt++; + ::assignmentQueueMutex.unlock(); while (nodeList->getNodeSocket()->receive((sockaddr *)&nodePublicAddress, packetData, &receivedBytes) && diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index 6985f21535..4fdbeed838 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -23,6 +23,7 @@ public: AudioMixerType, AvatarMixerType, AgentType, + VoxelServerType, AllTypes }; diff --git a/libraries/voxel-server-library/CMakeLists.txt b/libraries/voxel-server-library/CMakeLists.txt new file mode 100644 index 0000000000..9eabb95508 --- /dev/null +++ b/libraries/voxel-server-library/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 2.8) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +set(TARGET_NAME voxel-server-library) + +find_package(Qt5Widgets REQUIRED) + +include(${MACRO_DIR}/SetupHifiLibrary.cmake) +setup_hifi_library(${TARGET_NAME}) + +qt5_use_modules(${TARGET_NAME} Widgets) + +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} ${ROOT_DIR}) + +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) + +# link ZLIB +find_package(ZLIB) +include_directories(${ZLIB_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) + +# link in the shared library +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) + +# link in the hifi voxels library +link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) + +# link in the hifi avatars library +link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) diff --git a/voxel-server/src/NodeWatcher.cpp b/libraries/voxel-server-library/src/NodeWatcher.cpp similarity index 100% rename from voxel-server/src/NodeWatcher.cpp rename to libraries/voxel-server-library/src/NodeWatcher.cpp diff --git a/voxel-server/src/NodeWatcher.h b/libraries/voxel-server-library/src/NodeWatcher.h similarity index 100% rename from voxel-server/src/NodeWatcher.h rename to libraries/voxel-server-library/src/NodeWatcher.h diff --git a/voxel-server/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp similarity index 100% rename from voxel-server/src/VoxelNodeData.cpp rename to libraries/voxel-server-library/src/VoxelNodeData.cpp diff --git a/voxel-server/src/VoxelNodeData.h b/libraries/voxel-server-library/src/VoxelNodeData.h similarity index 100% rename from voxel-server/src/VoxelNodeData.h rename to libraries/voxel-server-library/src/VoxelNodeData.h diff --git a/voxel-server/src/VoxelPersistThread.cpp b/libraries/voxel-server-library/src/VoxelPersistThread.cpp similarity index 100% rename from voxel-server/src/VoxelPersistThread.cpp rename to libraries/voxel-server-library/src/VoxelPersistThread.cpp diff --git a/voxel-server/src/VoxelPersistThread.h b/libraries/voxel-server-library/src/VoxelPersistThread.h similarity index 100% rename from voxel-server/src/VoxelPersistThread.h rename to libraries/voxel-server-library/src/VoxelPersistThread.h diff --git a/voxel-server/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp similarity index 99% rename from voxel-server/src/VoxelSendThread.cpp rename to libraries/voxel-server-library/src/VoxelSendThread.cpp index 38f8c6f9b8..b9bb861349 100644 --- a/voxel-server/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -11,9 +11,12 @@ #include #include #include +#include +extern EnvironmentData environmentData[3]; #include "VoxelSendThread.h" #include "VoxelServer.h" +#include "VoxelServerState.h" VoxelSendThread::VoxelSendThread(uint16_t nodeID) : _nodeID(nodeID) { diff --git a/voxel-server/src/VoxelSendThread.h b/libraries/voxel-server-library/src/VoxelSendThread.h similarity index 100% rename from voxel-server/src/VoxelSendThread.h rename to libraries/voxel-server-library/src/VoxelSendThread.h diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp new file mode 100644 index 0000000000..b239a180d9 --- /dev/null +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -0,0 +1,349 @@ +// +// VoxelServer.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 9/16/13. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "VoxelNodeData.h" +#include +#include +#include +#include +#include + +#include "NodeWatcher.h" +#include "VoxelPersistThread.h" +#include "VoxelSendThread.h" +#include "VoxelServerPacketProcessor.h" + +#ifdef _WIN32 +#include "Syssocket.h" +#include "Systime.h" +#else +#include +#include +#include +#endif + +#include "VoxelServer.h" +#include "VoxelServerState.h" + +const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; +const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo"; +char voxelPersistFilename[MAX_FILENAME_LENGTH]; +int PACKETS_PER_CLIENT_PER_INTERVAL = 10; +VoxelTree serverTree(true); // this IS a reaveraging tree +bool wantVoxelPersist = true; +bool wantLocalDomain = false; +bool debugVoxelSending = false; +bool shouldShowAnimationDebug = false; +bool displayVoxelStats = false; +bool debugVoxelReceiving = false; +bool sendEnvironments = true; +bool sendMinimalEnvironment = false; +bool dumpVoxelsOnMove = false; +EnvironmentData environmentData[3]; +int receivedPacketCount = 0; +JurisdictionMap* jurisdiction = NULL; +JurisdictionSender* jurisdictionSender = NULL; +VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL; +VoxelPersistThread* voxelPersistThread = NULL; +pthread_mutex_t treeLock; +NodeWatcher nodeWatcher; // used to cleanup AGENT data when agents are killed + +int VoxelServer::_argc = 0; +const char** VoxelServer::_argv = NULL; +bool VoxelServer::_dontKillOnMissingDomain = false; + +void attachVoxelNodeDataToNode(Node* newNode) { + if (newNode->getLinkedData() == NULL) { + newNode->setLinkedData(new VoxelNodeData(newNode)); + } +} + +void VoxelServer::setArguments(int argc, char** argv) { + _argc = argc; + _argv = const_cast(argv); +} + +void VoxelServer::setupDomainAndPort(const char* domain, int port) { + NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, port); + + // Handle Local Domain testing with the --local command line + const char* local = "--local"; + ::wantLocalDomain = strcmp(domain, local) == 0; + if (::wantLocalDomain) { + printf("Local Domain MODE!\n"); + NodeList::getInstance()->setDomainIPToLocalhost(); + } else { + if (domain) { + NodeList::getInstance()->setDomainHostname(domain); + } + } + + // If we're running in standalone mode, we don't want to kill ourselves when we haven't heard from a domain + _dontKillOnMissingDomain = true; +} + +//int main(int argc, const char * argv[]) { +void VoxelServer::run() { + pthread_mutex_init(&::treeLock, NULL); + + qInstallMessageHandler(sharedMessageHandler); + + const char* JURISDICTION_FILE = "--jurisdictionFile"; + const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE); + if (jurisdictionFile) { + printf("jurisdictionFile=%s\n", jurisdictionFile); + + printf("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); + jurisdiction = new JurisdictionMap(jurisdictionFile); + printf("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); + } else { + const char* JURISDICTION_ROOT = "--jurisdictionRoot"; + const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT); + if (jurisdictionRoot) { + printf("jurisdictionRoot=%s\n", jurisdictionRoot); + } + + const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes"; + const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES); + if (jurisdictionEndNodes) { + printf("jurisdictionEndNodes=%s\n", jurisdictionEndNodes); + } + + if (jurisdictionRoot || jurisdictionEndNodes) { + ::jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); + } + } + + // should we send environments? Default is yes, but this command line suppresses sending + const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove"; + ::dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE); + printf("dumpVoxelsOnMove=%s\n", debug::valueOf(::dumpVoxelsOnMove)); + + // should we send environments? Default is yes, but this command line suppresses sending + const char* DONT_SEND_ENVIRONMENTS = "--dontSendEnvironments"; + bool dontSendEnvironments = getCmdOption(_argc, _argv, DONT_SEND_ENVIRONMENTS); + if (dontSendEnvironments) { + printf("Sending environments suppressed...\n"); + ::sendEnvironments = false; + } else { + // should we send environments? Default is yes, but this command line suppresses sending + const char* MINIMAL_ENVIRONMENT = "--MinimalEnvironment"; + ::sendMinimalEnvironment = getCmdOption(_argc, _argv, MINIMAL_ENVIRONMENT); + printf("Using Minimal Environment=%s\n", debug::valueOf(::sendMinimalEnvironment)); + } + printf("Sending environments=%s\n", debug::valueOf(::sendEnvironments)); + + NodeList* nodeList = NodeList::getInstance(); + nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER); + + setvbuf(stdout, NULL, _IOLBF, 0); + + // tell our NodeList about our desire to get notifications + nodeList->addHook(&nodeWatcher); + nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode; + + nodeList->startSilentNodeRemovalThread(); + srand((unsigned)time(0)); + + const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats"; + ::displayVoxelStats = getCmdOption(_argc, _argv, DISPLAY_VOXEL_STATS); + printf("displayVoxelStats=%s\n", debug::valueOf(::displayVoxelStats)); + + const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending"; + ::debugVoxelSending = getCmdOption(_argc, _argv, DEBUG_VOXEL_SENDING); + printf("debugVoxelSending=%s\n", debug::valueOf(::debugVoxelSending)); + + const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving"; + ::debugVoxelReceiving = getCmdOption(_argc, _argv, DEBUG_VOXEL_RECEIVING); + printf("debugVoxelReceiving=%s\n", debug::valueOf(::debugVoxelReceiving)); + + const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug"; + ::shouldShowAnimationDebug = getCmdOption(_argc, _argv, WANT_ANIMATION_DEBUG); + printf("shouldShowAnimationDebug=%s\n", debug::valueOf(::shouldShowAnimationDebug)); + + // By default we will voxel persist, if you want to disable this, then pass in this parameter + const char* NO_VOXEL_PERSIST = "--NoVoxelPersist"; + if ( getCmdOption(_argc, _argv, NO_VOXEL_PERSIST)) { + ::wantVoxelPersist = false; + } + printf("wantVoxelPersist=%s\n", debug::valueOf(::wantVoxelPersist)); + + // if we want Voxel Persistence, load the local file now... + bool persistantFileRead = false; + if (::wantVoxelPersist) { + + // Check to see if the user passed in a command line option for setting packet send rate + const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename"; + const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME); + if (voxelsPersistFilenameParameter) { + strcpy(voxelPersistFilename, voxelsPersistFilenameParameter); + } else { + //strcpy(voxelPersistFilename, ::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); + strcpy(voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE); + } + + printf("loading voxels from file: %s...\n", voxelPersistFilename); + + persistantFileRead = ::serverTree.readFromSVOFile(::voxelPersistFilename); + if (persistantFileRead) { + PerformanceWarning warn(::shouldShowAnimationDebug, + "persistVoxelsWhenDirty() - reaverageVoxelColors()", ::shouldShowAnimationDebug); + + // after done inserting all these voxels, then reaverage colors + serverTree.reaverageVoxelColors(serverTree.rootNode); + printf("Voxels reAveraged\n"); + } + + ::serverTree.clearDirtyBit(); // the tree is clean since we just loaded it + printf("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); + unsigned long nodeCount = ::serverTree.rootNode->getSubTreeNodeCount(); + unsigned long internalNodeCount = ::serverTree.rootNode->getSubTreeInternalNodeCount(); + unsigned long leafNodeCount = ::serverTree.rootNode->getSubTreeLeafNodeCount(); + printf("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); + + // now set up VoxelPersistThread + ::voxelPersistThread = new VoxelPersistThread(&::serverTree, ::voxelPersistFilename); + if (::voxelPersistThread) { + ::voxelPersistThread->initialize(true); + } + } + + // Check to see if the user passed in a command line option for loading an old style local + // Voxel File. If so, load it now. This is not the same as a voxel persist file + const char* INPUT_FILE = "-i"; + const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE); + if (voxelsFilename) { + serverTree.readFromSVOFile(voxelsFilename); + } + + // Check to see if the user passed in a command line option for setting packet send rate + const char* PACKETS_PER_SECOND = "--packetsPerSecond"; + const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND); + if (packetsPerSecond) { + PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/INTERVALS_PER_SECOND; + if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) { + PACKETS_PER_CLIENT_PER_INTERVAL = 1; + } + printf("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, PACKETS_PER_CLIENT_PER_INTERVAL); + } + + // for now, initialize the environments with fixed values + environmentData[1].setID(1); + environmentData[1].setGravity(1.0f); + environmentData[1].setAtmosphereCenter(glm::vec3(0.5, 0.5, (0.25 - 0.06125)) * (float)TREE_SCALE); + environmentData[1].setAtmosphereInnerRadius(0.030625f * TREE_SCALE); + environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f); + environmentData[2].setID(2); + environmentData[2].setGravity(1.0f); + environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE); + environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE); + environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); + environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue + + sockaddr senderAddress; + + unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; + ssize_t packetLength; + + timeval lastDomainServerCheckIn = {}; + + // set up our jurisdiction broadcaster... + ::jurisdictionSender = new JurisdictionSender(::jurisdiction); + if (::jurisdictionSender) { + ::jurisdictionSender->initialize(true); + } + + // set up our VoxelServerPacketProcessor + ::voxelServerPacketProcessor = new VoxelServerPacketProcessor(); + if (::voxelServerPacketProcessor) { + ::voxelServerPacketProcessor->initialize(true); + } + + // loop to send to nodes requesting data + while (true) { + + if (!_dontKillOnMissingDomain && + 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(); + } + + if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) && + packetVersionMatch(packetData)) { + + int numBytesPacketHeader = numBytesForPacketHeader(packetData); + + if (packetData[0] == PACKET_TYPE_HEAD_DATA) { + // If we got a PACKET_TYPE_HEAD_DATA, then we're talking to an NODE_TYPE_AVATAR, and we + // need to make sure we have it in our nodeList. + uint16_t nodeID = 0; + unpackNodeId(packetData + numBytesPacketHeader, &nodeID); + Node* node = NodeList::getInstance()->addOrUpdateNode(&senderAddress, + &senderAddress, + NODE_TYPE_AGENT, + nodeID); + + NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength); + } else if (packetData[0] == PACKET_TYPE_PING) { + // If the packet is a ping, let processNodeData handle it. + NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); + } else if (packetData[0] == PACKET_TYPE_DOMAIN) { + NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); + } else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { + if (::jurisdictionSender) { + ::jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength); + } + } else if (::voxelServerPacketProcessor) { + ::voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength); + } else { + printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]); + } + } + } + + if (::jurisdiction) { + delete ::jurisdiction; + } + + if (::jurisdictionSender) { + ::jurisdictionSender->terminate(); + delete ::jurisdictionSender; + } + + if (::voxelServerPacketProcessor) { + ::voxelServerPacketProcessor->terminate(); + delete ::voxelServerPacketProcessor; + } + + if (::voxelPersistThread) { + ::voxelPersistThread->terminate(); + delete ::voxelPersistThread; + } + + // tell our NodeList we're done with notifications + nodeList->removeHook(&nodeWatcher); + + pthread_mutex_destroy(&::treeLock); +} + + diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h new file mode 100644 index 0000000000..d51870dbb1 --- /dev/null +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -0,0 +1,36 @@ +// VoxelServer.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __voxel_server__VoxelServer__ +#define __voxel_server__VoxelServer__ + +/// Handles assignments of type VoxelServer - sending voxels to various clients. +class VoxelServer { +public: + /// runs the voxel server assignment + static void run(); + + /// allows setting of run arguments + static void setArguments(int argc, char** argv); + + /// when VoxelServer class is used by voxel-server stand alone executable it calls this to specify the domain + /// and port it is handling. When called by assignment-client, this is not needed because assignment-client + /// handles ports and domains automatically. + /// \param const char* domain domain name, IP address, or local to specify the domain the voxel server is serving + /// \param int port port the voxel server will listen on + static void setupDomainAndPort(const char* domain, int port); + +private: + static int _argc; + static const char** _argv; + static bool _dontKillOnMissingDomain; +}; + + + +#endif // __voxel_server__VoxelServer__ diff --git a/voxel-server/src/VoxelServerPacketProcessor.cpp b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp similarity index 99% rename from voxel-server/src/VoxelServerPacketProcessor.cpp rename to libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp index aaa347ee86..d283296e6f 100644 --- a/voxel-server/src/VoxelServerPacketProcessor.cpp +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp @@ -12,6 +12,7 @@ #include #include "VoxelServer.h" +#include "VoxelServerState.h" #include "VoxelServerPacketProcessor.h" diff --git a/voxel-server/src/VoxelServerPacketProcessor.h b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h similarity index 100% rename from voxel-server/src/VoxelServerPacketProcessor.h rename to libraries/voxel-server-library/src/VoxelServerPacketProcessor.h diff --git a/voxel-server/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServerState.h similarity index 88% rename from voxel-server/src/VoxelServer.h rename to libraries/voxel-server-library/src/VoxelServerState.h index 0f4c8577db..2d00d4a046 100644 --- a/voxel-server/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServerState.h @@ -6,12 +6,11 @@ // // -#ifndef __voxel_server__VoxelServer__ -#define __voxel_server__VoxelServer__ +#ifndef __voxel_server__VoxelServerState__ +#define __voxel_server__VoxelServerState__ #include #include // for MAX_PACKET_SIZE -#include #include #include @@ -19,7 +18,6 @@ const int MAX_FILENAME_LENGTH = 1024; -const int VOXEL_LISTEN_PORT = 40106; const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float)); const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES; const int MIN_BRIGHTNESS = 64; @@ -46,13 +44,10 @@ extern bool debugVoxelReceiving; extern bool sendEnvironments; extern bool sendMinimalEnvironment; extern bool dumpVoxelsOnMove; -extern EnvironmentData environmentData[3]; extern int receivedPacketCount; extern JurisdictionMap* jurisdiction; extern JurisdictionSender* jurisdictionSender; extern VoxelServerPacketProcessor* voxelServerPacketProcessor; extern pthread_mutex_t treeLock; - - -#endif // __voxel_server__VoxelServer__ +#endif // __voxel_server__VoxelServerState__ diff --git a/voxel-server/CMakeLists.txt b/voxel-server/CMakeLists.txt index c401a8033c..f759db0d89 100644 --- a/voxel-server/CMakeLists.txt +++ b/voxel-server/CMakeLists.txt @@ -26,3 +26,5 @@ link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) # link in the hifi avatars library link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) +# link in the hifi voxel-server-library +link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR}) diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 56ea5003e1..f854df84cf 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -6,73 +6,18 @@ // Copyright (c) 2012 High Fidelity, Inc. All rights reserved. // -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "VoxelNodeData.h" #include -#include -#include -#include -#include +#include +const int VOXEL_LISTEN_PORT = 40106; -#include "NodeWatcher.h" -#include "VoxelPersistThread.h" -#include "VoxelSendThread.h" -#include "VoxelServerPacketProcessor.h" - -#ifdef _WIN32 -#include "Syssocket.h" -#include "Systime.h" -#else -#include -#include -#include -#endif - -#include "VoxelServer.h" - -const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; -const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo"; -char voxelPersistFilename[MAX_FILENAME_LENGTH]; -int PACKETS_PER_CLIENT_PER_INTERVAL = 10; -VoxelTree serverTree(true); // this IS a reaveraging tree -bool wantVoxelPersist = true; -bool wantLocalDomain = false; -bool debugVoxelSending = false; -bool shouldShowAnimationDebug = false; -bool displayVoxelStats = false; -bool debugVoxelReceiving = false; -bool sendEnvironments = true; -bool sendMinimalEnvironment = false; -bool dumpVoxelsOnMove = false; -EnvironmentData environmentData[3]; -int receivedPacketCount = 0; -JurisdictionMap* jurisdiction = NULL; -JurisdictionSender* jurisdictionSender = NULL; -VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL; -VoxelPersistThread* voxelPersistThread = NULL; -pthread_mutex_t treeLock; -NodeWatcher nodeWatcher; // used to cleanup AGENT data when agents are killed - -void attachVoxelNodeDataToNode(Node* newNode) { - if (newNode->getLinkedData() == NULL) { - newNode->setLinkedData(new VoxelNodeData(newNode)); - } -} int main(int argc, const char * argv[]) { - pthread_mutex_init(&::treeLock, NULL); - - qInstallMessageHandler(sharedMessageHandler); - + + // Handle Local Domain testing with the --local command line + const char* local = "--local"; + bool wantLocalDomain = cmdOptionExists(argc, argv, local); + const char* domainIP = getCmdOption(argc, argv, "--domain"); + int listenPort = VOXEL_LISTEN_PORT; // Check to see if the user passed in a command line option for setting listen port const char* PORT_PARAMETER = "--port"; @@ -85,255 +30,16 @@ int main(int argc, const char * argv[]) { printf("portParameter=%s listenPort=%d\n", portParameter, listenPort); } - const char* JURISDICTION_FILE = "--jurisdictionFile"; - const char* jurisdictionFile = getCmdOption(argc, argv, JURISDICTION_FILE); - if (jurisdictionFile) { - printf("jurisdictionFile=%s\n", jurisdictionFile); - - printf("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); - jurisdiction = new JurisdictionMap(jurisdictionFile); - printf("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); + if (wantLocalDomain) { + VoxelServer::setupDomainAndPort(local, listenPort); } else { - const char* JURISDICTION_ROOT = "--jurisdictionRoot"; - const char* jurisdictionRoot = getCmdOption(argc, argv, JURISDICTION_ROOT); - if (jurisdictionRoot) { - printf("jurisdictionRoot=%s\n", jurisdictionRoot); - } - - const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes"; - const char* jurisdictionEndNodes = getCmdOption(argc, argv, JURISDICTION_ENDNODES); - if (jurisdictionEndNodes) { - printf("jurisdictionEndNodes=%s\n", jurisdictionEndNodes); - } - - if (jurisdictionRoot || jurisdictionEndNodes) { - ::jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); - } - } - - // should we send environments? Default is yes, but this command line suppresses sending - const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove"; - ::dumpVoxelsOnMove = cmdOptionExists(argc, argv, DUMP_VOXELS_ON_MOVE); - printf("dumpVoxelsOnMove=%s\n", debug::valueOf(::dumpVoxelsOnMove)); - - // should we send environments? Default is yes, but this command line suppresses sending - const char* DONT_SEND_ENVIRONMENTS = "--dontSendEnvironments"; - bool dontSendEnvironments = cmdOptionExists(argc, argv, DONT_SEND_ENVIRONMENTS); - if (dontSendEnvironments) { - printf("Sending environments suppressed...\n"); - ::sendEnvironments = false; - } else { - // should we send environments? Default is yes, but this command line suppresses sending - const char* MINIMAL_ENVIRONMENT = "--MinimalEnvironment"; - ::sendMinimalEnvironment = cmdOptionExists(argc, argv, MINIMAL_ENVIRONMENT); - printf("Using Minimal Environment=%s\n", debug::valueOf(::sendMinimalEnvironment)); - } - printf("Sending environments=%s\n", debug::valueOf(::sendEnvironments)); - - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort); - setvbuf(stdout, NULL, _IOLBF, 0); - - // tell our NodeList about our desire to get notifications - nodeList->addHook(&nodeWatcher); - - // Handle Local Domain testing with the --local command line - const char* local = "--local"; - ::wantLocalDomain = cmdOptionExists(argc, argv,local); - if (::wantLocalDomain) { - printf("Local Domain MODE!\n"); - nodeList->setDomainIPToLocalhost(); - } else { - const char* domainIP = getCmdOption(argc, argv, "--domain"); if (domainIP) { - NodeList::getInstance()->setDomainHostname(domainIP); + VoxelServer::setupDomainAndPort(domainIP, listenPort); } } - nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode; - nodeList->startSilentNodeRemovalThread(); - - srand((unsigned)time(0)); - - const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats"; - ::displayVoxelStats = cmdOptionExists(argc, argv, DISPLAY_VOXEL_STATS); - printf("displayVoxelStats=%s\n", debug::valueOf(::displayVoxelStats)); - - const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending"; - ::debugVoxelSending = cmdOptionExists(argc, argv, DEBUG_VOXEL_SENDING); - printf("debugVoxelSending=%s\n", debug::valueOf(::debugVoxelSending)); - - const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving"; - ::debugVoxelReceiving = cmdOptionExists(argc, argv, DEBUG_VOXEL_RECEIVING); - printf("debugVoxelReceiving=%s\n", debug::valueOf(::debugVoxelReceiving)); - - const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug"; - ::shouldShowAnimationDebug = cmdOptionExists(argc, argv, WANT_ANIMATION_DEBUG); - printf("shouldShowAnimationDebug=%s\n", debug::valueOf(::shouldShowAnimationDebug)); - - // By default we will voxel persist, if you want to disable this, then pass in this parameter - const char* NO_VOXEL_PERSIST = "--NoVoxelPersist"; - if (cmdOptionExists(argc, argv, NO_VOXEL_PERSIST)) { - ::wantVoxelPersist = false; - } - printf("wantVoxelPersist=%s\n", debug::valueOf(::wantVoxelPersist)); - - // if we want Voxel Persistence, load the local file now... - bool persistantFileRead = false; - if (::wantVoxelPersist) { - - // Check to see if the user passed in a command line option for setting packet send rate - const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename"; - const char* voxelsPersistFilenameParameter = getCmdOption(argc, argv, VOXELS_PERSIST_FILENAME); - if (voxelsPersistFilenameParameter) { - strcpy(voxelPersistFilename, voxelsPersistFilenameParameter); - } else { - strcpy(voxelPersistFilename, ::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); - } - - printf("loading voxels from file: %s...\n", voxelPersistFilename); - - persistantFileRead = ::serverTree.readFromSVOFile(::voxelPersistFilename); - if (persistantFileRead) { - PerformanceWarning warn(::shouldShowAnimationDebug, - "persistVoxelsWhenDirty() - reaverageVoxelColors()", ::shouldShowAnimationDebug); - - // after done inserting all these voxels, then reaverage colors - serverTree.reaverageVoxelColors(serverTree.rootNode); - printf("Voxels reAveraged\n"); - } - - ::serverTree.clearDirtyBit(); // the tree is clean since we just loaded it - printf("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); - unsigned long nodeCount = ::serverTree.rootNode->getSubTreeNodeCount(); - unsigned long internalNodeCount = ::serverTree.rootNode->getSubTreeInternalNodeCount(); - unsigned long leafNodeCount = ::serverTree.rootNode->getSubTreeLeafNodeCount(); - printf("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); - - // now set up VoxelPersistThread - ::voxelPersistThread = new VoxelPersistThread(&::serverTree, ::voxelPersistFilename); - if (::voxelPersistThread) { - ::voxelPersistThread->initialize(true); - } - } - - // Check to see if the user passed in a command line option for loading an old style local - // Voxel File. If so, load it now. This is not the same as a voxel persist file - const char* INPUT_FILE = "-i"; - const char* voxelsFilename = getCmdOption(argc, argv, INPUT_FILE); - if (voxelsFilename) { - serverTree.readFromSVOFile(voxelsFilename); - } - - // Check to see if the user passed in a command line option for setting packet send rate - const char* PACKETS_PER_SECOND = "--packetsPerSecond"; - const char* packetsPerSecond = getCmdOption(argc, argv, PACKETS_PER_SECOND); - if (packetsPerSecond) { - PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/INTERVALS_PER_SECOND; - if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) { - PACKETS_PER_CLIENT_PER_INTERVAL = 1; - } - printf("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, PACKETS_PER_CLIENT_PER_INTERVAL); - } - - // for now, initialize the environments with fixed values - environmentData[1].setID(1); - environmentData[1].setGravity(1.0f); - environmentData[1].setAtmosphereCenter(glm::vec3(0.5, 0.5, (0.25 - 0.06125)) * (float)TREE_SCALE); - environmentData[1].setAtmosphereInnerRadius(0.030625f * TREE_SCALE); - environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f); - environmentData[2].setID(2); - environmentData[2].setGravity(1.0f); - environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE); - environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE); - environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); - environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue - - sockaddr senderAddress; - - unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; - ssize_t packetLength; - - timeval lastDomainServerCheckIn = {}; - - // set up our jurisdiction broadcaster... - ::jurisdictionSender = new JurisdictionSender(::jurisdiction); - if (::jurisdictionSender) { - ::jurisdictionSender->initialize(true); - } - - // set up our VoxelServerPacketProcessor - ::voxelServerPacketProcessor = new VoxelServerPacketProcessor(); - if (::voxelServerPacketProcessor) { - ::voxelServerPacketProcessor->initialize(true); - } - - // loop to send to nodes requesting data - while (true) { - - // 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 (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) && - packetVersionMatch(packetData)) { - - int numBytesPacketHeader = numBytesForPacketHeader(packetData); - - if (packetData[0] == PACKET_TYPE_HEAD_DATA) { - // If we got a PACKET_TYPE_HEAD_DATA, then we're talking to an NODE_TYPE_AVATAR, and we - // need to make sure we have it in our nodeList. - uint16_t nodeID = 0; - unpackNodeId(packetData + numBytesPacketHeader, &nodeID); - Node* node = NodeList::getInstance()->addOrUpdateNode(&senderAddress, - &senderAddress, - NODE_TYPE_AGENT, - nodeID); - - NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength); - } else if (packetData[0] == PACKET_TYPE_PING) { - // If the packet is a ping, let processNodeData handle it. - NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); - } else if (packetData[0] == PACKET_TYPE_DOMAIN) { - NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); - } else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { - if (::jurisdictionSender) { - ::jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength); - } - } else if (::voxelServerPacketProcessor) { - ::voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength); - } else { - printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]); - } - } - } - - if (::jurisdiction) { - delete ::jurisdiction; - } - - if (::jurisdictionSender) { - ::jurisdictionSender->terminate(); - delete ::jurisdictionSender; - } - - if (::voxelServerPacketProcessor) { - ::voxelServerPacketProcessor->terminate(); - delete ::voxelServerPacketProcessor; - } - - if (::voxelPersistThread) { - ::voxelPersistThread->terminate(); - delete ::voxelPersistThread; - } - - // tell our NodeList we're done with notifications - nodeList->removeHook(&nodeWatcher); - - pthread_mutex_destroy(&::treeLock); - - return 0; + VoxelServer::setArguments(argc, const_cast(argv)); + VoxelServer::run(); }