Merge branch 'master' of https://github.com/worklist/hifi into blendface

This commit is contained in:
Andrzej Kapolka 2013-09-18 17:14:58 -07:00
commit 1bd8ea947a
48 changed files with 1031 additions and 752 deletions

View file

@ -14,7 +14,6 @@ set(CMAKE_AUTOMOC ON)
add_subdirectory(animation-server) add_subdirectory(animation-server)
add_subdirectory(assignment-client) add_subdirectory(assignment-client)
add_subdirectory(assignment-server)
add_subdirectory(domain-server) add_subdirectory(domain-server)
add_subdirectory(eve) add_subdirectory(eve)
add_subdirectory(interface) add_subdirectory(interface)

View file

@ -94,7 +94,7 @@ I want to run my own virtual world!
In order to set up your own virtual world, you need to set up and run your own In order to set up your own virtual world, you need to set up and run your own
local "domain". At a minimum, you must run a domain-server, voxel-server, local "domain". At a minimum, you must run a domain-server, voxel-server,
audio-mixer, and avatar-mixer to have a working virtual world. The audio-mixer and avatar-mixer are assignments given from the domain-server to any assignment-client that reports directly to it. audio-mixer, and avatar-mixer to have a working virtual world. The audio-mixer, avatar-mixer, and voxel-server are assignments given from the domain-server to any assignment-client that reports directly to it.
Complete the steps above to build the system components, using the default Cmake Unix Makefiles generator. Start with an empty build directory. Complete the steps above to build the system components, using the default Cmake Unix Makefiles generator. Start with an empty build directory.
@ -106,7 +106,7 @@ window, change directory into the build directory, make the needed components, a
First we make the targets we'll need. First we make the targets we'll need.
cd build cd build
make domain-server voxel-server assignment-client make domain-server assignment-client
If after this step you're seeing something like the following If after this step you're seeing something like the following
@ -114,35 +114,16 @@ If after this step you're seeing something like the following
you likely had Cmake generate Xcode project files and have not run `cmake ..` in a clean build directory. you likely had Cmake generate Xcode project files and have not run `cmake ..` in a clean build directory.
Then, launch the static components - a domain-server and a voxel-server. All of the targets will run in the foreground, so you'll either want to background it yourself or open a seperate terminal window per target. Then, launch the static domain-server. All of the targets will run in the foreground, so you'll either want to background it yourself or open a seperate terminal window per target.
cd domain-server && ./domain-server cd domain-server && ./domain-server
./voxel-server/voxel-server --local > /tmp/voxel-server.log 2>&1 &
Then, run an assignment-client with 2 forks to fulfill the avatar-mixer and audio-mixer assignments. It uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port). Then, run an assignment-client with 3 forks to fulfill the avatar-mixer, audio-mixer, and voxel-server assignments. It uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port).
./assignment-client/assignment-client -n 2 -a localhost -p 40102 ./assignment-client/assignment-client -n 3
Any target can be terminated with CTRL-C (SIGINT) in the associated terminal window. Any target can be terminated with CTRL-C (SIGINT) in the associated terminal window.
Determine the IP address of the machine you're running these servers on. Here's
a handy resource that explains how to do this for different operating systems.
http://kb.iu.edu/data/aapa.html
On Mac OS X, and many Unix systems you can use the ifconfig command. Typically,
the following command will give you the IP address you need to use.
ifconfig | grep inet | grep broadcast
You should get something like this:
inet 192.168.1.104 netmask 0xffffff00 broadcast 192.168.1.255
Your IP address is the first set of numbers. In this case "192.168.1.104". You
may now use this IP address to access your domain. If you are running a local
DNS or other name service you should be able to access this IP address by name
as well.
To test things out you'll want to run the Interface client. You can make that target with the following command: To test things out you'll want to run the Interface client. You can make that target with the following command:
make interface make interface
@ -150,9 +131,8 @@ To test things out you'll want to run the Interface client. You can make that ta
Then run the executable it builds, or open interface.app if you're on OS X. Then run the executable it builds, or open interface.app if you're on OS X.
To access your local domain in Interface, open the Preferences dialog box, from To access your local domain in Interface, open the Preferences dialog box, from
the Interface menu on OS X or the File menu on Linux, and enter the IP address of the local DNS name for the the Interface menu on OS X or the File menu on Linux, and enter "localhost" for the
server computer in the "Domain" edit control. server hostname in the "Domain" edit control.
In the voxel-server/src directory you will find a README that explains in In the voxel-server/src directory you will find a README that explains in
further detail how to setup and administer a voxel-server. further detail how to setup and administer a voxel-server.

View file

@ -685,9 +685,9 @@ int main(int argc, const char * argv[])
nodeList->setDomainIPToLocalhost(); nodeList->setDomainIPToLocalhost();
} }
const char* domainIP = getCmdOption(argc, argv, "--domain"); const char* domainHostname = getCmdOption(argc, argv, "--domain");
if (domainIP) { if (domainHostname) {
NodeList::getInstance()->setDomainIP(domainIP); NodeList::getInstance()->setDomainHostname(domainHostname);
} }
nodeList->linkedDataCreateCallback = NULL; // do we need a callback? nodeList->linkedDataCreateCallback = NULL; // do we need a callback?

View file

@ -24,4 +24,12 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR})
# link the stk library
set(STK_ROOT_DIR ${ROOT_DIR}/externals/stk)
find_package(STK REQUIRED)
target_link_libraries(${TARGET_NAME} ${STK_LIBRARIES})
include_directories(${STK_INCLUDE_DIRS})

View file

@ -9,24 +9,28 @@
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <QtNetwork/QtNetwork> #include <QtNetwork/QtNetwork>
#include <AvatarData.h>
#include <NodeList.h> #include <NodeList.h>
#include "AvatarData.h"
#include "Agent.h" #include "Agent.h"
#include "voxels/VoxelScriptingInterface.h"
Agent::Agent() : Agent::Agent(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
_shouldStop(false)
{
} }
void Agent::run(QUrl scriptURL) { void Agent::run() {
NodeList::getInstance()->setOwnerType(NODE_TYPE_AGENT); NodeList::getInstance()->setOwnerType(NODE_TYPE_AGENT);
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_AVATAR_MIXER, 1); NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1);
QNetworkAccessManager manager; QNetworkAccessManager manager;
// 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(),
this->getUUIDStringWithoutCurlyBraces());
QUrl scriptURL(scriptURLString);
qDebug() << "Attemping download of " << scriptURL << "\n"; qDebug() << "Attemping download of " << scriptURL << "\n";
QNetworkReply* reply = manager.get(QNetworkRequest(scriptURL)); QNetworkReply* reply = manager.get(QNetworkRequest(scriptURL));
@ -39,14 +43,13 @@ void Agent::run(QUrl scriptURL) {
QScriptEngine engine; QScriptEngine engine;
AvatarData *testAvatarData = new AvatarData;
QScriptValue avatarDataValue = engine.newQObject(testAvatarData);
engine.globalObject().setProperty("Avatar", avatarDataValue);
QScriptValue agentValue = engine.newQObject(this); QScriptValue agentValue = engine.newQObject(this);
engine.globalObject().setProperty("Agent", agentValue); engine.globalObject().setProperty("Agent", agentValue);
VoxelScriptingInterface voxelScripter;
QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter);
engine.globalObject().setProperty("Voxels", voxelScripterValue);
qDebug() << "Downloaded script:" << scriptString << "\n"; qDebug() << "Downloaded script:" << scriptString << "\n";
qDebug() << "Evaluated script:" << engine.evaluate(scriptString).toString() << "\n"; qDebug() << "Evaluated script:" << engine.evaluate(scriptString).toString() << "\n";
@ -75,9 +78,10 @@ void Agent::run(QUrl scriptURL) {
NodeList::getInstance()->sendDomainServerCheckIn(); NodeList::getInstance()->sendDomainServerCheckIn();
} }
// allow the scripter's call back to setup visual data
emit preSendCallback(); emit preSendCallback();
// flush the voxel packet queue
testAvatarData->sendData(); voxelScripter.getVoxelPacketSender()->process();
if (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { if (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) {
NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes);

View file

@ -9,19 +9,19 @@
#ifndef __hifi__Agent__ #ifndef __hifi__Agent__
#define __hifi__Agent__ #define __hifi__Agent__
#include "SharedUtil.h"
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QUrl> #include <QtCore/QUrl>
class Agent : public QObject { #include <Assignment.h>
class Agent : public Assignment {
Q_OBJECT Q_OBJECT
public: public:
Agent(); Agent(const unsigned char* dataBuffer, int numBytes);
bool volatile _shouldStop; bool volatile _shouldStop;
void run(QUrl scriptUrl); void run();
signals: signals:
void preSendCallback(); void preSendCallback();
}; };

View file

@ -0,0 +1,36 @@
//
// AssignmentFactory.cpp
// hifi
//
// Created by Stephen Birarda on 9/17/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <PacketHeaders.h>
#include "Agent.h"
#include "audio/AudioMixer.h"
#include "avatars/AvatarMixer.h"
#include <VoxelServer.h>
#include "AssignmentFactory.h"
Assignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) {
int headerBytes = numBytesForPacketHeader(dataBuffer);
Assignment::Type assignmentType = Assignment::AllTypes;
memcpy(&assignmentType, dataBuffer + headerBytes, sizeof(Assignment::Type));
switch (assignmentType) {
case Assignment::AudioMixerType:
return new AudioMixer(dataBuffer, numBytes);
case Assignment::AvatarMixerType:
return new AvatarMixer(dataBuffer, numBytes);
case Assignment::AgentType:
return new Agent(dataBuffer, numBytes);
case Assignment::VoxelServerType:
return new VoxelServer(dataBuffer, numBytes);
default:
return new Assignment(dataBuffer, numBytes);
}
}

View file

@ -0,0 +1,19 @@
//
// AssignmentFactory.h
// hifi
//
// Created by Stephen Birarda on 9/17/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__AssignmentFactory__
#define __hifi__AssignmentFactory__
#include "Assignment.h"
class AssignmentFactory {
public:
static Assignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes);
};
#endif /* defined(__hifi__AssignmentFactory__) */

View file

@ -69,6 +69,10 @@ void attachNewBufferToNode(Node *newNode) {
} }
} }
AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
}
void AudioMixer::run() { void AudioMixer::run() {
// change the logging target name while this is running // change the logging target name while this is running
Logging::setTargetName(AUDIO_MIXER_LOGGING_TARGET_NAME); Logging::setTargetName(AUDIO_MIXER_LOGGING_TARGET_NAME);
@ -125,7 +129,7 @@ void AudioMixer::run() {
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed // 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) { if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL); gettimeofday(&lastDomainServerCheckIn, NULL);
NodeList::getInstance()->sendDomainServerCheckIn(); NodeList::getInstance()->sendDomainServerCheckIn(this->getUUID().toRfc4122().constData());
if (Logging::shouldSendStats() && numStatCollections > 0) { if (Logging::shouldSendStats() && numStatCollections > 0) {
// if we should be sending stats to Logstash send the appropriate average now // if we should be sending stats to Logstash send the appropriate average now
@ -421,7 +425,7 @@ void AudioMixer::run() {
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);
} else { } else {
std::cout << "Took too much time, not sleeping!\n"; qDebug("Took too much time, not sleeping!\n");
} }
} }
} }

View file

@ -9,11 +9,15 @@
#ifndef __hifi__AudioMixer__ #ifndef __hifi__AudioMixer__
#define __hifi__AudioMixer__ #define __hifi__AudioMixer__
#include <Assignment.h>
/// Handles assignments of type AudioMixer - mixing streams of audio and re-distributing to various clients. /// Handles assignments of type AudioMixer - mixing streams of audio and re-distributing to various clients.
class AudioMixer { class AudioMixer : public Assignment {
public: public:
AudioMixer(const unsigned char* dataBuffer, int numBytes);
/// runs the audio mixer /// runs the audio mixer
static void run(); void run();
}; };
#endif /* defined(__hifi__AudioMixer__) */ #endif /* defined(__hifi__AudioMixer__) */

View file

@ -83,6 +83,10 @@ void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) {
nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket); nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
} }
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
}
void AvatarMixer::run() { void AvatarMixer::run() {
// change the logging target name while AvatarMixer is running // change the logging target name while AvatarMixer is running
Logging::setTargetName(AVATAR_MIXER_LOGGING_NAME); Logging::setTargetName(AVATAR_MIXER_LOGGING_NAME);
@ -115,7 +119,7 @@ void AvatarMixer::run() {
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed // 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) { if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL); gettimeofday(&lastDomainServerCheckIn, NULL);
NodeList::getInstance()->sendDomainServerCheckIn(); NodeList::getInstance()->sendDomainServerCheckIn(this->getUUID().toRfc4122().constData());
} }
if (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) && if (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&

View file

@ -9,13 +9,15 @@
#ifndef __hifi__AvatarMixer__ #ifndef __hifi__AvatarMixer__
#define __hifi__AvatarMixer__ #define __hifi__AvatarMixer__
#include <iostream> #include <Assignment.h>
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients /// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
class AvatarMixer { class AvatarMixer : public Assignment {
public: public:
AvatarMixer(const unsigned char* dataBuffer, int numBytes);
/// runs the avatar mixer /// runs the avatar mixer
static void run(); void run();
}; };
#endif /* defined(__hifi__AvatarMixer__) */ #endif /* defined(__hifi__AvatarMixer__) */

View file

@ -14,16 +14,19 @@
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include "Agent.h"
#include <Assignment.h>
#include <AudioMixer.h>
#include <AvatarMixer.h>
#include <Logging.h> #include <Logging.h>
#include <NodeList.h> #include <NodeList.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <VoxelServer.h> #include <VoxelServer.h>
#include "Agent.h"
#include "Assignment.h"
#include "AssignmentFactory.h"
#include "audio/AudioMixer.h"
#include "avatars/AvatarMixer.h"
const long long ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000; const long long ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000;
const char PARENT_TARGET_NAME[] = "assignment-client-monitor"; const char PARENT_TARGET_NAME[] = "assignment-client-monitor";
const char CHILD_TARGET_NAME[] = "assignment-client"; const char CHILD_TARGET_NAME[] = "assignment-client";
@ -31,6 +34,7 @@ const char CHILD_TARGET_NAME[] = "assignment-client";
pid_t* childForks = NULL; pid_t* childForks = NULL;
sockaddr_in customAssignmentSocket = {}; sockaddr_in customAssignmentSocket = {};
int numForks = 0; int numForks = 0;
Assignment::Type overiddenAssignmentType = Assignment::AllTypes;
void childClient() { void childClient() {
// this is one of the child forks or there is a single assignment client, continue assignment-client execution // this is one of the child forks or there is a single assignment client, continue assignment-client execution
@ -56,8 +60,8 @@ void childClient() {
sockaddr_in senderSocket = {}; sockaddr_in senderSocket = {};
// create a request assignment, accept all assignments, pass the desired pool (if it exists) // create a request assignment, accept assignments defined by the overidden type
Assignment requestAssignment(Assignment::RequestCommand, Assignment::AllTypes); Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType);
while (true) { while (true) {
if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) { if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) {
@ -72,51 +76,40 @@ void childClient() {
&& packetVersionMatch(packetData)) { && packetVersionMatch(packetData)) {
// construct the deployed assignment from the packet data // construct the deployed assignment from the packet data
Assignment deployedAssignment(packetData, receivedBytes); Assignment* deployedAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes);
qDebug() << "Received an assignment -" << deployedAssignment << "\n"; qDebug() << "Received an assignment -" << *deployedAssignment << "\n";
// switch our nodelist DOMAIN_IP // switch our nodelist DOMAIN_IP
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT || if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT ||
deployedAssignment.getAttachedPublicSocket()->sa_family == AF_INET) { deployedAssignment->getAttachedPublicSocket()->sa_family == AF_INET) {
in_addr domainSocketAddr = {};
sockaddr* domainSocket = NULL;
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
// the domain server IP address is the address we got this packet from // the domain server IP address is the address we got this packet from
domainSocketAddr = senderSocket.sin_addr; domainSocket = (sockaddr*) &senderSocket;
} else { } else {
// grab the domain server IP address from the packet from the AS // grab the domain server IP address from the packet from the AS
domainSocketAddr = ((sockaddr_in*) deployedAssignment.getAttachedPublicSocket())->sin_addr; domainSocket = (sockaddr*) deployedAssignment->getAttachedPublicSocket();
} }
nodeList->setDomainIP(inet_ntoa(domainSocketAddr)); nodeList->setDomainIP(QHostAddress(domainSocket));
qDebug("Destination IP for assignment is %s\n", inet_ntoa(domainSocketAddr)); qDebug("Destination IP for assignment is %s\n", nodeList->getDomainIP().toString().toStdString().c_str());
if (deployedAssignment.getType() == Assignment::AudioMixerType) { // run the deployed assignment
AudioMixer::run(); deployedAssignment->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");
scriptURLString = scriptURLString.arg(inet_ntoa(domainSocketAddr),
deployedAssignment.getUUIDStringWithoutCurlyBraces());
qDebug() << "Starting an Agent assignment-client with script at" << scriptURLString << "\n";
Agent scriptAgent;
scriptAgent.run(QUrl(scriptURLString));
}
} else { } else {
qDebug("Received a bad destination socket for assignment.\n"); qDebug("Received a bad destination socket for assignment.\n");
} }
qDebug("Assignment finished or never started - waiting for new 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 // reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
nodeList->clear(); nodeList->clear();
@ -161,6 +154,7 @@ void sigchldHandler(int sig) {
} }
} }
} }
} }
void parentMonitor() { void parentMonitor() {
@ -206,11 +200,20 @@ int main(int argc, const char* argv[]) {
if (customAssignmentServerHostname) { if (customAssignmentServerHostname) {
const char* customAssignmentServerPortString = getCmdOption(argc, argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION); const char* customAssignmentServerPortString = getCmdOption(argc, argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION);
unsigned short assignmentServerPort = customAssignmentServerPortString unsigned short assignmentServerPort = customAssignmentServerPortString
? atoi(customAssignmentServerPortString) : ASSIGNMENT_SERVER_PORT; ? atoi(customAssignmentServerPortString) : DEFAULT_DOMAIN_SERVER_PORT;
::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServerHostname, assignmentServerPort); ::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServerHostname, assignmentServerPort);
} }
const char ASSIGNMENT_TYPE_OVVERIDE_OPTION[] = "-t";
const char* assignmentTypeString = getCmdOption(argc, argv, ASSIGNMENT_TYPE_OVVERIDE_OPTION);
if (assignmentTypeString) {
// the user is asking to only be assigned to a particular type of assignment
// so set that as the ::overridenAssignmentType to be used in requests
::overiddenAssignmentType = (Assignment::Type) atoi(assignmentTypeString);
}
const char* NUM_FORKS_PARAMETER = "-n"; const char* NUM_FORKS_PARAMETER = "-n";
const char* numForksString = getCmdOption(argc, argv, NUM_FORKS_PARAMETER); const char* numForksString = getCmdOption(argc, argv, NUM_FORKS_PARAMETER);

View file

@ -0,0 +1,15 @@
//
// VoxelScriptingInterface.cpp
// hifi
//
// Created by Stephen Birarda on 9/17/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include "VoxelScriptingInterface.h"
void VoxelScriptingInterface::queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue) {
// setup a VoxelDetail struct with the data
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
_voxelPacketSender.sendVoxelEditMessage(PACKET_TYPE_SET_VOXEL, addVoxelDetail);
}

View file

@ -0,0 +1,36 @@
//
// VoxelScriptingInterface.h
// hifi
//
// Created by Stephen Birarda on 9/17/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__VoxelScriptingInterface__
#define __hifi__VoxelScriptingInterface__
#include <QtCore/QObject>
#include <VoxelEditPacketSender.h>
/// handles scripting of voxel commands from JS passed to assigned clients
class VoxelScriptingInterface : public QObject {
Q_OBJECT
public:
VoxelEditPacketSender* getVoxelPacketSender() { return &_voxelPacketSender; }
public slots:
/// queues the creation of a voxel which will be sent by calling process on the PacketSender
/// \param x the x-coordinate of the voxel (in VS space)
/// \param y the y-coordinate of the voxel (in VS space)
/// \param z the z-coordinate of the voxel (in VS space)
/// \param scale the scale of the voxel (in VS space)
/// \param red the R value for RGB color of voxel
/// \param green the G value for RGB color of voxel
/// \param blue the B value for RGB color of voxel
void queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue);
private:
/// attached VoxelEditPacketSender that handles queuing and sending of packets to VS
VoxelEditPacketSender _voxelPacketSender;
};
#endif /* defined(__hifi__VoxelScriptingInterface__) */

View file

@ -1,13 +0,0 @@
cmake_minimum_required(VERSION 2.8)
set(TARGET_NAME assignment-server)
set(ROOT_DIR ..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
include(${MACRO_DIR}/SetupHifiProject.cmake)
setup_hifi_project(${TARGET_NAME} TRUE)
# link in the shared library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})

View file

@ -1,108 +0,0 @@
//
// main.cpp
// assignment-server
//
// Created by Stephen Birarda on 7/1/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <arpa/inet.h>
#include <fstream>
#include <deque>
#include <QtCore/QString>
#include <Assignment.h>
#include <Logging.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UDPSocket.h>
const int MAX_PACKET_SIZE_BYTES = 1400;
const long long NUM_DEFAULT_ASSIGNMENT_STALENESS_USECS = 10 * 1000 * 1000;
int main(int argc, const char* argv[]) {
qInstallMessageHandler(Logging::verboseMessageHandler);
std::deque<Assignment*> assignmentQueue;
sockaddr_in senderSocket;
unsigned char senderData[MAX_PACKET_SIZE_BYTES] = {};
ssize_t receivedBytes = 0;
UDPSocket serverSocket(ASSIGNMENT_SERVER_PORT);
unsigned char assignmentPacket[MAX_PACKET_SIZE_BYTES];
int numSendHeaderBytes = populateTypeAndVersion(assignmentPacket, PACKET_TYPE_DEPLOY_ASSIGNMENT);
while (true) {
if (serverSocket.receive((sockaddr*) &senderSocket, &senderData, &receivedBytes)) {
if (senderData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
// construct the requested assignment from the packet data
Assignment requestAssignment(senderData, receivedBytes);
qDebug() << "Received request for assignment:" << requestAssignment << "\n";
qDebug() << "Current queue size is" << assignmentQueue.size() << "\n";
// make sure there are assignments in the queue at all
if (assignmentQueue.size() > 0) {
std::deque<Assignment*>::iterator assignment = assignmentQueue.begin();
// enumerate assignments until we find one to give this client (if possible)
while (assignment != assignmentQueue.end()) {
// if this assignment is stale then get rid of it and check the next one
if (usecTimestampNow() - usecTimestamp(&((*assignment)->getTime()))
>= NUM_DEFAULT_ASSIGNMENT_STALENESS_USECS) {
delete *assignment;
assignment = assignmentQueue.erase(assignment);
continue;
}
// check if the requestor is on the same network as the destination for the assignment
if (senderSocket.sin_addr.s_addr ==
((sockaddr_in*) (*assignment)->getAttachedPublicSocket())->sin_addr.s_addr) {
// if this is the case we remove the public socket on the assignment by setting it to NULL
// this ensures the local IP and port sent to the requestor is the local address of destination
(*assignment)->setAttachedPublicSocket(NULL);
}
int numAssignmentBytes = (*assignment)->packToBuffer(assignmentPacket + numSendHeaderBytes);
// send the assignment
serverSocket.send((sockaddr*) &senderSocket,
assignmentPacket,
numSendHeaderBytes + numAssignmentBytes);
// delete this assignment now that it has been sent out
delete *assignment;
// remove it from the deque and make the iterator the next assignment
assignmentQueue.erase(assignment);
// stop looping - we've handed out an assignment
break;
}
}
} else if (senderData[0] == PACKET_TYPE_CREATE_ASSIGNMENT && packetVersionMatch(senderData)) {
// construct the create assignment from the packet data
Assignment* createdAssignment = new Assignment(senderData, receivedBytes);
qDebug() << "Received a created assignment:" << *createdAssignment << "\n";
qDebug() << "Current queue size is" << assignmentQueue.size() << "\n";
// assignment server is likely on a public server
// assume that the address we now have for the sender is the public address/port
// and store that with the assignment so it can be given to the requestor later if necessary
createdAssignment->setAttachedPublicSocket((sockaddr*) &senderSocket);
// add this assignment to the queue
assignmentQueue.push_back(createdAssignment);
}
}
}
}

View file

@ -2,7 +2,15 @@ MACRO(SETUP_HIFI_PROJECT TARGET INCLUDE_QT)
project(${TARGET}) project(${TARGET})
# grab the implemenation and header files # grab the implemenation and header files
file(GLOB TARGET_SRCS src/*.cpp src/*.h src/*.c) file(GLOB TARGET_SRCS src/*)
file(GLOB SRC_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/*)
foreach(DIR ${SRC_SUBDIRS})
if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/${DIR})
FILE(GLOB DIR_CONTENTS src/${DIR}/*)
SET(TARGET_SRCS ${TARGET_SRCS} ${DIR_CONTENTS})
endif()
endforeach()
# add the executable # add the executable
add_executable(${TARGET} ${TARGET_SRCS}) add_executable(${TARGET} ${TARGET_SRCS})

View file

@ -36,11 +36,22 @@ body {
} }
#deploy-button { #deploy-button {
background-color: #0DFFBB; background-color: #0DFFBB;
right: 0px; right: 85px;
} }
#deploy-button:hover { #deploy-button:hover {
background-color: #28FF57; background-color: #28FF57;
} }
#instance-field {
position: absolute;
right: 20px;
top: 40px;
}
#instance-field input {
width: 80px;
}
#stop-button { #stop-button {
background-color: #CC1F00; background-color: #CC1F00;
right: 0px; right: 0px;

View file

@ -14,6 +14,9 @@
Run Run
</a> </a>
</div> </div>
<div class='big-field' id='instance-field'>
<input type='text' name='instances' placeholder='# of instances'>
</div>
<!-- %div#stop-button.big-button --> <!-- %div#stop-button.big-button -->
</body> </body>
</html> </html>

View file

@ -20,6 +20,11 @@ $(document).ready(function(){
// add the script // add the script
+ script + '\r\n' + script + '\r\n'
+ '--' + boundary + '--'; + '--' + boundary + '--';
var headers = {};
if ($('#instance-field input').val()) {
headers['ASSIGNMENT-INSTANCES'] = $('#instance-field input').val();
}
// post form to assignment in order to create an assignment // post form to assignment in order to create an assignment
$.ajax({ $.ajax({
@ -27,6 +32,7 @@ $(document).ready(function(){
data: body, data: body,
type: "POST", type: "POST",
url: "/assignment", url: "/assignment",
headers: headers,
success: function (data, status) { success: function (data, status) {
console.log(data); console.log(data);
console.log(status); console.log(status);

View file

@ -27,6 +27,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QMap>
#include <QtCore/QMutex> #include <QtCore/QMutex>
#include <civetweb.h> #include <civetweb.h>
@ -58,7 +59,7 @@ unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* no
} }
static int mongooseRequestHandler(struct mg_connection *conn) { static int mongooseRequestHandler(struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn); const struct mg_request_info* ri = mg_get_request_info(conn);
if (strcmp(ri->uri, "/assignment") == 0 && strcmp(ri->request_method, "POST") == 0) { if (strcmp(ri->uri, "/assignment") == 0 && strcmp(ri->request_method, "POST") == 0) {
// return a 200 // return a 200
@ -76,9 +77,20 @@ static int mongooseRequestHandler(struct mg_connection *conn) {
const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment"; const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment";
static void mongooseUploadHandler(struct mg_connection *conn, const char *path) { static void mongooseUploadHandler(struct mg_connection *conn, const char *path) {
// create an assignment for this saved script, for now make it local only // create an assignment for this saved script, for now make it local only
Assignment *scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, Assignment::LocalLocation); Assignment *scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, Assignment::LocalLocation);
// check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header
const char ASSIGNMENT_INSTANCES_HTTP_HEADER[] = "ASSIGNMENT-INSTANCES";
const char *requestInstancesHeader = mg_get_header(conn, ASSIGNMENT_INSTANCES_HTTP_HEADER);
if (requestInstancesHeader) {
// the user has requested a number of instances greater than 1
// so set that on the created assignment
scriptAssignment->setNumberOfInstances(atoi(requestInstancesHeader));
}
QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION); QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION);
newPath += "/"; newPath += "/";
// append the UUID for this script as the new filename, remove the curly braces // append the UUID for this script as the new filename, remove the curly braces
@ -94,7 +106,6 @@ static void mongooseUploadHandler(struct mg_connection *conn, const char *path)
::assignmentQueueMutex.lock(); ::assignmentQueueMutex.lock();
::assignmentQueue.push_back(scriptAssignment); ::assignmentQueue.push_back(scriptAssignment);
::assignmentQueueMutex.unlock(); ::assignmentQueueMutex.unlock();
} }
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
@ -103,7 +114,11 @@ int main(int argc, const char* argv[]) {
qInstallMessageHandler(Logging::verboseMessageHandler); qInstallMessageHandler(Logging::verboseMessageHandler);
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, DOMAIN_LISTEN_PORT); const char CUSTOM_PORT_OPTION[] = "-p";
const char* customPortString = getCmdOption(argc, argv, CUSTOM_PORT_OPTION);
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DOMAIN_LISTEN_PORT;
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort);
setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0);
@ -121,20 +136,8 @@ int main(int argc, const char* argv[]) {
in_addr_t serverLocalAddress = getLocalAddress(); in_addr_t serverLocalAddress = getLocalAddress();
nodeList->startSilentNodeRemovalThread(); nodeList->startSilentNodeRemovalThread();
timeval lastStatSendTime = {}; timeval lastStatSendTime = {};
const char ASSIGNMENT_SERVER_OPTION[] = "-a";
// grab the overriden assignment-server hostname from argv, if it exists
const char* customAssignmentServer = getCmdOption(argc, argv, ASSIGNMENT_SERVER_OPTION);
if (customAssignmentServer) {
sockaddr_in customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServer, ASSIGNMENT_SERVER_PORT);
nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket);
}
// use a map to keep track of iterations of silence for assignment creation requests
const long long GLOBAL_ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000;
timeval lastGlobalAssignmentRequest = {};
// as a domain-server we will always want an audio mixer and avatar mixer // as a domain-server we will always want an audio mixer and avatar mixer
// setup the create assignments for those // setup the create assignments for those
@ -145,10 +148,20 @@ int main(int argc, const char* argv[]) {
Assignment avatarMixerAssignment(Assignment::CreateCommand, Assignment avatarMixerAssignment(Assignment::CreateCommand,
Assignment::AvatarMixerType, Assignment::AvatarMixerType,
Assignment::LocalLocation); Assignment::LocalLocation);
Assignment voxelServerAssignment(Assignment::CreateCommand, Assignment voxelServerAssignment(Assignment::CreateCommand,
Assignment::VoxelServerType, Assignment::VoxelServerType,
Assignment::LocalLocation); Assignment::LocalLocation);
// Handle Domain/Voxel Server configuration command line arguments
const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig";
const char* voxelServerConfig = getCmdOption(argc, argv, VOXEL_CONFIG_OPTION);
if (voxelServerConfig) {
qDebug("Reading Voxel Server Configuration.\n");
qDebug() << " config: " << voxelServerConfig << "\n";
int payloadLength = strlen(voxelServerConfig) + sizeof(char);
voxelServerAssignment.setPayload((const uchar*)voxelServerConfig, payloadLength);
}
// construct a local socket to send with our created assignments to the global AS // construct a local socket to send with our created assignments to the global AS
sockaddr_in localSocket = {}; sockaddr_in localSocket = {};
@ -157,13 +170,13 @@ int main(int argc, const char* argv[]) {
localSocket.sin_addr.s_addr = serverLocalAddress; localSocket.sin_addr.s_addr = serverLocalAddress;
// setup the mongoose web server // setup the mongoose web server
struct mg_context *ctx; struct mg_context* ctx;
struct mg_callbacks callbacks = {}; struct mg_callbacks callbacks = {};
QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath()); QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
// list of options. Last element must be NULL. // list of options. Last element must be NULL.
const char *options[] = {"listening_ports", "8080", const char* options[] = {"listening_ports", "8080",
"document_root", documentRoot.toStdString().c_str(), NULL}; "document_root", documentRoot.toStdString().c_str(), NULL};
callbacks.begin_request = mongooseRequestHandler; callbacks.begin_request = mongooseRequestHandler;
@ -204,10 +217,9 @@ int main(int argc, const char* argv[]) {
} }
} }
const int MIN_VOXEL_SERVER_CHECKS = 10; const int MIN_VOXEL_SERVER_CHECKS = 10;
if (checkForVoxelServerAttempt > MIN_VOXEL_SERVER_CHECKS && if (checkForVoxelServerAttempt > MIN_VOXEL_SERVER_CHECKS && voxelServerCount == 0 &&
voxelServerCount == 0 &&
std::find(::assignmentQueue.begin(), ::assignmentQueue.end(), &voxelServerAssignment) == ::assignmentQueue.end()) { std::find(::assignmentQueue.begin(), ::assignmentQueue.end(), &voxelServerAssignment) == ::assignmentQueue.end()) {
qDebug("Missing a Voxel Server and assignment not in queue. Adding.\n"); qDebug("Missing a voxel server and assignment not in queue. Adding.\n");
::assignmentQueue.push_front(&voxelServerAssignment); ::assignmentQueue.push_front(&voxelServerAssignment);
} }
checkForVoxelServerAttempt++; checkForVoxelServerAttempt++;
@ -237,77 +249,107 @@ int main(int argc, const char* argv[]) {
nodePublicAddress.sin_addr.s_addr = 0; nodePublicAddress.sin_addr.s_addr = 0;
} }
Node* newNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress, bool matchedUUID = true;
(sockaddr*) &nodeLocalAddress,
nodeType,
nodeList->getLastNodeID());
// if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it if ((nodeType == NODE_TYPE_AVATAR_MIXER || nodeType == NODE_TYPE_AUDIO_MIXER) &&
if (newNode) { !nodeList->soloNodeOfType(nodeType)) {
if (newNode->getNodeID() == nodeList->getLastNodeID()) { // if this is an audio-mixer or an avatar-mixer and we don't have one yet
nodeList->increaseNodeID(); // 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<Assignment*>::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++;
}
} }
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN); // unlock the assignment queue
::assignmentQueueMutex.unlock();
}
if (matchedUUID) {
Node* newNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress,
(sockaddr*) &nodeLocalAddress,
nodeType,
nodeList->getLastNodeID());
currentBufferPos = broadcastPacket + numHeaderBytes; // if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it
startPointer = currentBufferPos; if (newNode) {
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + sizeof(NODE_TYPE) nodeList->increaseNodeID();
+ numBytesSocket + sizeof(unsigned char); }
int numInterestTypes = *(nodeTypesOfInterest - 1);
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back currentBufferPos = broadcastPacket + numHeaderBytes;
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { startPointer = currentBufferPos;
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) { int numBytesUUID = (nodeType == NODE_TYPE_AUDIO_MIXER || nodeType == NODE_TYPE_AVATAR_MIXER)
// this is not the node themselves ? NUM_BYTES_RFC4122_UUID
// and this is an node of a type in the passed node types of interest : 0;
// or the node did not pass us any specific types they are interested in
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + numBytesUUID +
if (memchr(SOLO_NODE_TYPES, node->getType(), sizeof(SOLO_NODE_TYPES)) == NULL) { sizeof(NODE_TYPE) + numBytesSocket + sizeof(unsigned char);
// this is an node of which there can be multiple, just add them to the packet 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 // don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) { if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node)); currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
} }
} else {
// solo node, we need to only send newest
if (newestSoloNodes[node->getType()] == NULL ||
newestSoloNodes[node->getType()]->getWakeMicrostamp() < node->getWakeMicrostamp()) {
// we have to set the newer solo node to add it to the broadcast later
newestSoloNodes[node->getType()] = &(*node);
}
} }
} }
} }
for (std::map<char, Node *>::iterator soloNode = newestSoloNodes.begin(); // update last receive to now
soloNode != newestSoloNodes.end(); uint64_t timeNow = usecTimestampNow();
soloNode++) { newNode->setLastHeardMicrostamp(timeNow);
// this is the newest alive solo node, add them to the packet
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, soloNode->second); // 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();
newNode->setLastHeardMicrostamp(timeNow);
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
&& memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
newNode->setWakeMicrostamp(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);
} }
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) { } else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
@ -320,65 +362,65 @@ int main(int argc, const char* argv[]) {
std::deque<Assignment*>::iterator assignment = ::assignmentQueue.begin(); std::deque<Assignment*>::iterator assignment = ::assignmentQueue.begin();
while (assignment != ::assignmentQueue.end()) { while (assignment != ::assignmentQueue.end()) {
// construct the requested assignment from the packet data
Assignment requestAssignment(packetData, receivedBytes);
// give this assignment out, no conditions stop us from giving it to the local assignment client if (requestAssignment.getType() == Assignment::AllTypes ||
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT); (*assignment)->getType() == requestAssignment.getType()) {
int numAssignmentBytes = (*assignment)->packToBuffer(broadcastPacket + numHeaderBytes); // attach our local socket to the assignment
(*assignment)->setAttachedLocalSocket((sockaddr*) &localSocket);
nodeList->getNodeSocket()->send((sockaddr*) &nodePublicAddress,
broadcastPacket, // give this assignment out, either the type matches or the requestor said they will take any
numHeaderBytes + numAssignmentBytes); int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT);
int numAssignmentBytes = (*assignment)->packToBuffer(broadcastPacket + numHeaderBytes);
// remove the assignment from the queue
::assignmentQueue.erase(assignment); nodeList->getNodeSocket()->send((sockaddr*) &nodePublicAddress,
broadcastPacket,
if ((*assignment)->getType() == Assignment::AgentType) { numHeaderBytes + numAssignmentBytes);
// if this is a script assignment we need to delete it to avoid a memory leak
delete *assignment; if ((*assignment)->getType() == Assignment::AgentType) {
// if this is a script assignment we need to delete it to avoid a memory leak
// or if there is more than one instance to send out, simpy decrease the number of instances
if ((*assignment)->getNumberOfInstances() > 1) {
(*assignment)->decrementNumberOfInstances();
} else {
::assignmentQueue.erase(assignment);
delete *assignment;
}
} else {
// remove the assignment from the queue
::assignmentQueue.erase(assignment);
if ((*assignment)->getType() != Assignment::VoxelServerType) {
// keep audio-mixer and avatar-mixer assignments in the queue
// until we get a check-in from that GUID
// but stick it at the back so the others have a chance to go out
::assignmentQueue.push_back(*assignment);
}
}
// stop looping, we've handed out an assignment
break;
} else {
// push forward the iterator to check the next assignment
assignment++;
} }
// stop looping, we've handed out an assignment
break;
} }
::assignmentQueueMutex.unlock(); ::assignmentQueueMutex.unlock();
} } 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
// if ASSIGNMENT_REQUEST_INTERVAL_USECS have passed since last global assignment request then fire off another
if (usecTimestampNow() - usecTimestamp(&lastGlobalAssignmentRequest) >= GLOBAL_ASSIGNMENT_REQUEST_INTERVAL_USECS) {
gettimeofday(&lastGlobalAssignmentRequest, NULL);
::assignmentQueueMutex.lock();
// go through our queue and see if there are any assignments to send to the global assignment server
std::deque<Assignment*>::iterator assignment = ::assignmentQueue.begin();
while (assignment != assignmentQueue.end()) {
if ((*assignment)->getLocation() != Assignment::LocalLocation) { // unpack it
// attach our local socket to the assignment so the assignment-server can optionally hand it out Assignment* createAssignment = new Assignment(packetData, receivedBytes);
(*assignment)->setAttachedLocalSocket((sockaddr*) &localSocket);
qDebug() << "Received a create assignment -" << *createAssignment << "\n";
nodeList->sendAssignment(*(*assignment));
// add the assignment at the back of the queue
// remove the assignment from the queue ::assignmentQueueMutex.lock();
::assignmentQueue.erase(assignment); ::assignmentQueue.push_back(createAssignment);
::assignmentQueueMutex.unlock();
if ((*assignment)->getType() == Assignment::AgentType) {
// if this is a script assignment we need to delete it to avoid a memory leak
delete *assignment;
}
// stop looping, we've handed out an assignment
break;
} else {
// push forward the iterator to check the next assignment
assignment++;
}
} }
::assignmentQueueMutex.unlock();
} }
if (Logging::shouldSendStats()) { if (Logging::shouldSendStats()) {

View file

@ -183,18 +183,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
// --domain or --local options // --domain or --local options
NodeList::getInstance()->loadData(_settings); NodeList::getInstance()->loadData(_settings);
const char* domainIP = getCmdOption(argc, constArgv, "--domain");
if (domainIP) {
NodeList::getInstance()->setDomainIP(domainIP);
}
// Handle Local Domain testing with the --local command line
if (cmdOptionExists(argc, constArgv, "--local")) {
qDebug("Local Domain MODE!\n");
NodeList::getInstance()->setDomainIPToLocalhost();
}
// Check to see if the user passed in a command line option for loading a local // Check to see if the user passed in a command line option for loading a local
// Voxel File. // Voxel File.
_voxelsFilename = getCmdOption(argc, constArgv, "-i"); _voxelsFilename = getCmdOption(argc, constArgv, "-i");

View file

@ -17,9 +17,8 @@
#define SETTINGS_VERSION_KEY "info-version" #define SETTINGS_VERSION_KEY "info-version"
#define MAX_DIALOG_HEIGHT_RATIO 0.9 #define MAX_DIALOG_HEIGHT_RATIO 0.9
InfoView::InfoView(bool forced) { InfoView::InfoView(bool forced) :
_forced = forced; _forced(forced) {
settings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
switchToResourcesParentIfRequired(); switchToResourcesParentIfRequired();
QString absPath = QFileInfo("resources/html/interface-welcome-allsvg.html").absoluteFilePath(); QString absPath = QFileInfo("resources/html/interface-welcome-allsvg.html").absoluteFilePath();

View file

@ -688,6 +688,48 @@ void Menu::aboutApp() {
InfoView::forcedShow(); InfoView::forcedShow();
} }
void updateDSHostname(const QString& domainServerHostname) {
QString newHostname(DEFAULT_DOMAIN_HOSTNAME);
if (domainServerHostname.size() > 0) {
// the user input a new hostname, use that
newHostname = domainServerHostname;
}
// check if the domain server hostname is new
if (NodeList::getInstance()->getDomainHostname() != newHostname) {
NodeList::getInstance()->clear();
// kill the local voxels
Application::getInstance()->getVoxels()->killLocalVoxels();
// reset the environment to default
Application::getInstance()->getEnvironment()->resetToDefault();
// set the new hostname
NodeList::getInstance()->setDomainHostname(newHostname);
}
}
const int QLINE_MINIMUM_WIDTH = 400;
QLineEdit* lineEditForDomainHostname() {
QString currentDomainHostname = NodeList::getInstance()->getDomainHostname();
if (NodeList::getInstance()->getDomainPort() != DEFAULT_DOMAIN_SERVER_PORT) {
// add the port to the currentDomainHostname string if it is custom
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainPort()));
}
QLineEdit* domainServerLineEdit = new QLineEdit(currentDomainHostname);
domainServerLineEdit->setPlaceholderText(DEFAULT_DOMAIN_HOSTNAME);
domainServerLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
return domainServerLineEdit;
}
void Menu::editPreferences() { void Menu::editPreferences() {
Application* applicationInstance = Application::getInstance(); Application* applicationInstance = Application::getInstance();
QDialog dialog(applicationInstance->getGLWidget()); QDialog dialog(applicationInstance->getGLWidget());
@ -698,11 +740,8 @@ void Menu::editPreferences() {
QFormLayout* form = new QFormLayout(); QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1); layout->addLayout(form, 1);
const int QLINE_MINIMUM_WIDTH = 400; QLineEdit* domainServerLineEdit = lineEditForDomainHostname();
form->addRow("Domain server:", domainServerLineEdit);
QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname()));
domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Domain server:", domainServerHostname);
QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString()); QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString());
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH); avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
@ -743,30 +782,7 @@ void Menu::editPreferences() {
return; return;
} }
QByteArray newHostname; updateDSHostname(domainServerLineEdit->text());
if (domainServerHostname->text().size() > 0) {
// the user input a new hostname, use that
newHostname = domainServerHostname->text().toLocal8Bit();
} else {
// the user left the field blank, use the default hostname
newHostname = QByteArray(DEFAULT_DOMAIN_HOSTNAME);
}
// check if the domain server hostname is new
if (memcmp(NodeList::getInstance()->getDomainHostname(), newHostname.constData(), newHostname.size()) != 0) {
NodeList::getInstance()->clear();
// kill the local voxels
applicationInstance->getVoxels()->killLocalVoxels();
// reset the environment to default
applicationInstance->getEnvironment()->resetToDefault();
// set the new hostname
NodeList::getInstance()->setDomainHostname(newHostname.constData());
}
QUrl avatarVoxelURL(avatarURL->text()); QUrl avatarVoxelURL(avatarURL->text());
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL); applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
@ -798,12 +814,10 @@ void Menu::goToDomain() {
QFormLayout* form = new QFormLayout(); QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1); layout->addLayout(form, 1);
const int QLINE_MINIMUM_WIDTH = 400; QLineEdit* domainServerLineEdit = lineEditForDomainHostname();
form->addRow("Domain server:", domainServerLineEdit);
QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname()));
domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Domain server:", domainServerHostname);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
@ -816,30 +830,7 @@ void Menu::goToDomain() {
return; return;
} }
QByteArray newHostname; updateDSHostname(domainServerLineEdit->text());
if (domainServerHostname->text().size() > 0) {
// the user input a new hostname, use that
newHostname = domainServerHostname->text().toLocal8Bit();
} else {
// the user left the field blank, use the default hostname
newHostname = QByteArray(DEFAULT_DOMAIN_HOSTNAME);
}
// check if the domain server hostname is new
if (memcmp(NodeList::getInstance()->getDomainHostname(), newHostname.constData(), newHostname.size()) != 0) {
NodeList::getInstance()->clear();
// kill the local voxels
applicationInstance->getVoxels()->killLocalVoxels();
// reset the environment to default
applicationInstance->getEnvironment()->resetToDefault();
// set the new hostname
NodeList::getInstance()->setDomainHostname(newHostname.constData());
}
} }
void Menu::goToLocation() { void Menu::goToLocation() {

View file

@ -16,10 +16,4 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR}) include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake) include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link the stk library
set(STK_ROOT_DIR ${ROOT_DIR}/externals/stk)
find_package(STK REQUIRED)
target_link_libraries(${TARGET_NAME} ${STK_LIBRARIES})
include_directories(${STK_INCLUDE_DIRS})

View file

@ -6,9 +6,13 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
set(TARGET_NAME shared) set(TARGET_NAME shared)
project(${TARGET_NAME}) project(${TARGET_NAME})
find_package(Qt5Network REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake) include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME}) setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Network)
set(EXTERNAL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external) set(EXTERNAL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external)
if (WIN32) if (WIN32)

View file

@ -14,14 +14,15 @@
const char IPv4_ADDRESS_DESIGNATOR = 4; const char IPv4_ADDRESS_DESIGNATOR = 4;
const char IPv6_ADDRESS_DESIGNATOR = 6; const char IPv6_ADDRESS_DESIGNATOR = 6;
const int NUM_BYTES_RFC4122_UUID = 16;
Assignment::Assignment(Assignment::Command command, Assignment::Type type, Assignment::Location location) : Assignment::Assignment(Assignment::Command command, Assignment::Type type, Assignment::Location location) :
_command(command), _command(command),
_type(type), _type(type),
_location(location), _location(location),
_attachedPublicSocket(NULL), _attachedPublicSocket(NULL),
_attachedLocalSocket(NULL) _attachedLocalSocket(NULL),
_numberOfInstances(1),
_payload(NULL),
_numPayloadBytes(0)
{ {
// set the create time on this assignment // set the create time on this assignment
gettimeofday(&_time, NULL); gettimeofday(&_time, NULL);
@ -35,7 +36,10 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, Assig
Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) : Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
_location(GlobalLocation), _location(GlobalLocation),
_attachedPublicSocket(NULL), _attachedPublicSocket(NULL),
_attachedLocalSocket(NULL) _attachedLocalSocket(NULL),
_numberOfInstances(1),
_payload(NULL),
_numPayloadBytes(0)
{ {
// set the create time on this assignment // set the create time on this assignment
gettimeofday(&_time, NULL); gettimeofday(&_time, NULL);
@ -52,41 +56,67 @@ Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
numBytesRead += numBytesForPacketHeader(dataBuffer); numBytesRead += numBytesForPacketHeader(dataBuffer);
memcpy(&_type, dataBuffer + numBytesRead, sizeof(Assignment::Type));
numBytesRead += sizeof(Assignment::Type);
if (dataBuffer[0] != PACKET_TYPE_REQUEST_ASSIGNMENT) { if (dataBuffer[0] != PACKET_TYPE_REQUEST_ASSIGNMENT) {
// read the GUID for this assignment // read the GUID for this assignment
_uuid = QUuid::fromRfc4122(QByteArray((const char*) dataBuffer + numBytesRead, NUM_BYTES_RFC4122_UUID)); _uuid = QUuid::fromRfc4122(QByteArray((const char*) dataBuffer + numBytesRead, NUM_BYTES_RFC4122_UUID));
numBytesRead += NUM_BYTES_RFC4122_UUID; numBytesRead += NUM_BYTES_RFC4122_UUID;
} }
memcpy(&_type, dataBuffer + numBytesRead, sizeof(Assignment::Type)); if (_command != Assignment::RequestCommand) {
numBytesRead += sizeof(Assignment::Type);
if (numBytes > numBytesRead) {
sockaddr* newSocket = NULL; sockaddr* newSocket = NULL;
if (dataBuffer[numBytesRead++] == IPv4_ADDRESS_DESIGNATOR) { if (dataBuffer[numBytesRead++] == IPv4_ADDRESS_DESIGNATOR) {
// IPv4 address // IPv4 address
newSocket = (sockaddr*) new sockaddr_in; newSocket = (sockaddr*) new sockaddr_in;
unpackSocket(dataBuffer + numBytesRead, newSocket); numBytesRead += unpackSocket(dataBuffer + numBytesRead, newSocket);
if (_command == Assignment::CreateCommand) {
delete _attachedLocalSocket;
_attachedLocalSocket = newSocket;
} else {
delete _attachedPublicSocket;
_attachedPublicSocket = newSocket;
}
} else { } else {
// IPv6 address, or bad designator // IPv6 address, or bad designator
qDebug("Received a socket that cannot be unpacked!\n"); qDebug("Received a socket that cannot be unpacked!\n");
} }
}
if (_command == Assignment::CreateCommand) {
delete _attachedLocalSocket; if (numBytes > numBytesRead) {
_attachedLocalSocket = newSocket; _numPayloadBytes = numBytes - numBytesRead;
} else { _payload = new uchar[_numPayloadBytes];
delete _attachedPublicSocket; memcpy(_payload, dataBuffer + numBytesRead, _numPayloadBytes);
_attachedPublicSocket = newSocket;
}
} }
} }
Assignment::~Assignment() { Assignment::~Assignment() {
delete _attachedPublicSocket; delete _attachedPublicSocket;
delete _attachedLocalSocket; delete _attachedLocalSocket;
delete[] _payload;
_numPayloadBytes = 0;
}
const int MAX_PAYLOAD_BYTES = 1024;
void Assignment::setPayload(const uchar* payload, int numBytes) {
if (numBytes > MAX_PAYLOAD_BYTES) {
qDebug("Set payload called with number of bytes greater than maximum (%d). Will only transfer %d bytes.\n",
MAX_PAYLOAD_BYTES,
MAX_PAYLOAD_BYTES);
_numPayloadBytes = 1024;
} else {
_numPayloadBytes = numBytes;
}
delete[] _payload;
_payload = new uchar[_numPayloadBytes];
memcpy(_payload, payload, _numPayloadBytes);
} }
QString Assignment::getUUIDStringWithoutCurlyBraces() const { QString Assignment::getUUIDStringWithoutCurlyBraces() const {
@ -120,15 +150,15 @@ void Assignment::setAttachedLocalSocket(const sockaddr* attachedLocalSocket) {
int Assignment::packToBuffer(unsigned char* buffer) { int Assignment::packToBuffer(unsigned char* buffer) {
int numPackedBytes = 0; int numPackedBytes = 0;
// pack the UUID for this assignment, if this is an assignment create or deploy
if (_command != Assignment::RequestCommand) {
memcpy(buffer, _uuid.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
numPackedBytes += NUM_BYTES_RFC4122_UUID;
}
memcpy(buffer + numPackedBytes, &_type, sizeof(_type)); memcpy(buffer + numPackedBytes, &_type, sizeof(_type));
numPackedBytes += sizeof(_type); numPackedBytes += sizeof(_type);
// pack the UUID for this assignment, if this is an assignment create or deploy
if (_command != Assignment::RequestCommand) {
memcpy(buffer + numPackedBytes, _uuid.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
numPackedBytes += NUM_BYTES_RFC4122_UUID;
}
if (_attachedPublicSocket || _attachedLocalSocket) { if (_attachedPublicSocket || _attachedLocalSocket) {
sockaddr* socketToPack = (_attachedPublicSocket) ? _attachedPublicSocket : _attachedLocalSocket; sockaddr* socketToPack = (_attachedPublicSocket) ? _attachedPublicSocket : _attachedLocalSocket;
@ -138,10 +168,17 @@ int Assignment::packToBuffer(unsigned char* buffer) {
numPackedBytes += packSocket(buffer + numPackedBytes, socketToPack); numPackedBytes += packSocket(buffer + numPackedBytes, socketToPack);
} }
if (_numPayloadBytes) {
memcpy(buffer + numPackedBytes, _payload, _numPayloadBytes);
numPackedBytes += _numPayloadBytes;
}
return numPackedBytes; return numPackedBytes;
} }
void Assignment::run() {
// run method ovveridden by subclasses
}
QDebug operator<<(QDebug debug, const Assignment &assignment) { QDebug operator<<(QDebug debug, const Assignment &assignment) {
debug << "T:" << assignment.getType(); debug << "T:" << assignment.getType();
return debug.nospace(); return debug.nospace();

View file

@ -15,8 +15,11 @@
#include "NodeList.h" #include "NodeList.h"
const int NUM_BYTES_RFC4122_UUID = 16;
/// Holds information used for request, creation, and deployment of assignments /// Holds information used for request, creation, and deployment of assignments
class Assignment { class Assignment : public QObject {
Q_OBJECT
public: public:
enum Type { enum Type {
@ -56,12 +59,20 @@ public:
Assignment::Location getLocation() const { return _location; } Assignment::Location getLocation() const { return _location; }
const timeval& getTime() const { return _time; } const timeval& getTime() const { return _time; }
uchar* getPayload() { return _payload; }
int getNumPayloadBytes() const { return _numPayloadBytes; }
void setPayload(const uchar *payload, int numBytes);
int getNumberOfInstances() const { return _numberOfInstances; }
void setNumberOfInstances(int numberOfInstances) { _numberOfInstances = numberOfInstances; }
void decrementNumberOfInstances() { --_numberOfInstances; }
const sockaddr* getAttachedPublicSocket() { return _attachedPublicSocket; } const sockaddr* getAttachedPublicSocket() { return _attachedPublicSocket; }
void setAttachedPublicSocket(const sockaddr* attachedPublicSocket); void setAttachedPublicSocket(const sockaddr* attachedPublicSocket);
const sockaddr* getAttachedLocalSocket() { return _attachedLocalSocket; } const sockaddr* getAttachedLocalSocket() { return _attachedLocalSocket; }
void setAttachedLocalSocket(const sockaddr* attachedLocalSocket); void setAttachedLocalSocket(const sockaddr* attachedLocalSocket);
/// Packs the assignment to the passed buffer /// Packs the assignment to the passed buffer
/// \param buffer the buffer in which to pack the assignment /// \param buffer the buffer in which to pack the assignment
/// \return number of bytes packed into buffer /// \return number of bytes packed into buffer
@ -70,6 +81,9 @@ public:
/// Sets _time to the current time given by gettimeofday /// Sets _time to the current time given by gettimeofday
void setCreateTimeToNow() { gettimeofday(&_time, NULL); } void setCreateTimeToNow() { gettimeofday(&_time, NULL); }
/// blocking run of the assignment
virtual void run();
private: private:
QUuid _uuid; /// the 16 byte UUID for this assignment QUuid _uuid; /// the 16 byte UUID for this assignment
Assignment::Command _command; /// the command for this assignment (Create, Deploy, Request) Assignment::Command _command; /// the command for this assignment (Create, Deploy, Request)
@ -78,6 +92,9 @@ private:
sockaddr* _attachedPublicSocket; /// pointer to a public socket that relates to assignment, depends on direction sockaddr* _attachedPublicSocket; /// pointer to a public socket that relates to assignment, depends on direction
sockaddr* _attachedLocalSocket; /// pointer to a local socket that relates to assignment, depends on direction sockaddr* _attachedLocalSocket; /// pointer to a local socket that relates to assignment, depends on direction
timeval _time; /// time the assignment was created (set in constructor) timeval _time; /// time the assignment was created (set in constructor)
int _numberOfInstances; /// the number of instances of this assignment
uchar *_payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed
int _numPayloadBytes; /// number of bytes in the payload, up to a maximum of 1024
}; };
QDebug operator<<(QDebug debug, const Assignment &assignment); QDebug operator<<(QDebug debug, const Assignment &assignment);

View file

@ -12,6 +12,7 @@
#include <cstdio> #include <cstdio>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtNetwork/QHostInfo>
#include "Assignment.h" #include "Assignment.h"
#include "Logging.h" #include "Logging.h"
@ -31,9 +32,8 @@ const char SOLO_NODE_TYPES[2] = {
NODE_TYPE_AUDIO_MIXER NODE_TYPE_AUDIO_MIXER
}; };
const char DEFAULT_DOMAIN_HOSTNAME[MAX_HOSTNAME_BYTES] = "root.highfidelity.io"; const QString DEFAULT_DOMAIN_HOSTNAME = "root.highfidelity.io";
const char DEFAULT_DOMAIN_IP[INET_ADDRSTRLEN] = ""; // IP Address will be re-set by lookup on startup const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
const int DEFAULT_DOMAINSERVER_PORT = 40102;
bool silentNodeThreadStopFlag = false; bool silentNodeThreadStopFlag = false;
bool pingUnknownNodeThreadStopFlag = false; bool pingUnknownNodeThreadStopFlag = false;
@ -59,6 +59,9 @@ NodeList* NodeList::getInstance() {
} }
NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
_domainHostname(DEFAULT_DOMAIN_HOSTNAME),
_domainIP(),
_domainPort(DEFAULT_DOMAIN_SERVER_PORT),
_nodeBuckets(), _nodeBuckets(),
_numNodes(0), _numNodes(0),
_nodeSocket(newSocketListenPort), _nodeSocket(newSocketListenPort),
@ -69,8 +72,7 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
_numNoReplyDomainCheckIns(0), _numNoReplyDomainCheckIns(0),
_assignmentServerSocket(NULL) _assignmentServerSocket(NULL)
{ {
memcpy(_domainHostname, DEFAULT_DOMAIN_HOSTNAME, sizeof(DEFAULT_DOMAIN_HOSTNAME));
memcpy(_domainIP, DEFAULT_DOMAIN_IP, sizeof(DEFAULT_DOMAIN_IP));
} }
NodeList::~NodeList() { NodeList::~NodeList() {
@ -82,22 +84,29 @@ NodeList::~NodeList() {
stopSilentNodeRemovalThread(); stopSilentNodeRemovalThread();
} }
void NodeList::setDomainHostname(const char* domainHostname) { void NodeList::setDomainHostname(const QString& domainHostname) {
memset(_domainHostname, 0, sizeof(_domainHostname));
memcpy(_domainHostname, domainHostname, strlen(domainHostname));
// reset the domain IP so the hostname is checked again int colonIndex = domainHostname.indexOf(':');
setDomainIP("");
} if (colonIndex > 0) {
// the user has included a custom DS port with the hostname
void NodeList::setDomainIP(const char* domainIP) {
memset(_domainIP, 0, sizeof(_domainIP)); // the new hostname is everything up to the colon
memcpy(_domainIP, domainIP, strlen(domainIP)); _domainHostname = domainHostname.left(colonIndex);
}
// grab the port by reading the string after the colon
void NodeList::setDomainIPToLocalhost() { _domainPort = atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData());
int ip = getLocalAddress();
sprintf(_domainIP, "%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainPort << "\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;
}
// reset our _domainIP to the null address so that a lookup happens on next check in
_domainIP.clear();
} }
void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
@ -117,10 +126,7 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat
switch (packetData[0]) { switch (packetData[0]) {
case PACKET_TYPE_DOMAIN: { case PACKET_TYPE_DOMAIN: {
// only process the DS if this is our current domain server // only process the DS if this is our current domain server
sockaddr_in domainServerSocket = *(sockaddr_in*) senderAddress; if (_domainIP == QHostAddress(senderAddress)) {
const char* domainSenderIP = inet_ntoa(domainServerSocket.sin_addr);
if (memcmp(domainSenderIP, _domainIP, strlen(domainSenderIP)) == 0) {
processDomainServerList(packetData, dataBytes); processDomainServerList(packetData, dataBytes);
} }
@ -268,23 +274,33 @@ void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNo
_nodeTypesOfInterest[numNodeTypesOfInterest] = '\0'; _nodeTypesOfInterest[numNodeTypesOfInterest] = '\0';
} }
void NodeList::sendDomainServerCheckIn() { void NodeList::sendDomainServerCheckIn(const char* assignmentUUID) {
static bool printedDomainServerIP = false; static bool printedDomainServerIP = false;
// Lookup the IP address of the domain server if we need to // Lookup the IP address of the domain server if we need to
if (atoi(_domainIP) == 0) { if (_domainIP.isNull()) {
printf("Looking up %s\n", _domainHostname); qDebug("Looking up DS hostname %s.\n", _domainHostname.toStdString().c_str());
struct hostent* pHostInfo;
if ((pHostInfo = gethostbyname(_domainHostname)) != NULL) { QHostInfo domainServerHostInfo = QHostInfo::fromName(_domainHostname);
sockaddr_in tempAddress;
memcpy(&tempAddress.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length); for (int i = 0; i < domainServerHostInfo.addresses().size(); i++) {
strcpy(_domainIP, inet_ntoa(tempAddress.sin_addr)); if (domainServerHostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
qDebug("Domain Server: %s\n", _domainHostname); _domainIP = domainServerHostInfo.addresses()[i];
} else {
qDebug("Failed domain server lookup\n"); qDebug("DS at %s is at %s\n", _domainHostname.toStdString().c_str(), _domainIP.toString().toStdString().c_str());
printedDomainServerIP = true;
break;
}
// if we got here without a break out of the for loop then we failed to lookup the address
if (i == domainServerHostInfo.addresses().size() - 1) {
qDebug("Failed domain server lookup\n");
}
} }
} else if (!printedDomainServerIP) { } else if (!printedDomainServerIP) {
qDebug("Domain Server IP: %s\n", _domainIP); qDebug("Domain Server IP: %s\n", _domainIP.toString().toStdString().c_str());
printedDomainServerIP = true; printedDomainServerIP = true;
} }
@ -297,9 +313,9 @@ void NodeList::sendDomainServerCheckIn() {
const int IP_ADDRESS_BYTES = 4; const int IP_ADDRESS_BYTES = 4;
// check in packet has header, node type, port, IP, node types of interest, null termination // check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination
int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) + sizeof(uint16_t) + int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) +
IP_ADDRESS_BYTES + numBytesNodesOfInterest + sizeof(unsigned char); NUM_BYTES_RFC4122_UUID + sizeof(uint16_t) + IP_ADDRESS_BYTES + numBytesNodesOfInterest + sizeof(unsigned char);
checkInPacket = new unsigned char[numPacketBytes]; checkInPacket = new unsigned char[numPacketBytes];
unsigned char* packetPosition = checkInPacket; unsigned char* packetPosition = checkInPacket;
@ -313,7 +329,13 @@ void NodeList::sendDomainServerCheckIn() {
*(packetPosition++) = _ownerType; *(packetPosition++) = _ownerType;
packetPosition += packSocket(checkInPacket + numHeaderBytes + sizeof(NODE_TYPE), if (assignmentUUID) {
// if we've got an assignment UUID to send add that here
memcpy(packetPosition, assignmentUUID, NUM_BYTES_RFC4122_UUID);
packetPosition += NUM_BYTES_RFC4122_UUID;
}
packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket),
getLocalAddress(), getLocalAddress(),
htons(_nodeSocket.getListeningPort())); htons(_nodeSocket.getListeningPort()));
@ -331,7 +353,7 @@ void NodeList::sendDomainServerCheckIn() {
checkInPacketSize = packetPosition - checkInPacket; checkInPacketSize = packetPosition - checkInPacket;
} }
_nodeSocket.send(_domainIP, DEFAULT_DOMAINSERVER_PORT, checkInPacket, checkInPacketSize); _nodeSocket.send(_domainIP.toString().toStdString().c_str(), _domainPort, checkInPacket, checkInPacketSize);
// increment the count of un-replied check-ins // increment the count of un-replied check-ins
_numNoReplyDomainCheckIns++; _numNoReplyDomainCheckIns++;
@ -364,7 +386,7 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte
// if the public socket address is 0 then it's reachable at the same IP // if the public socket address is 0 then it's reachable at the same IP
// as the domain server // as the domain server
if (nodePublicSocket.sin_addr.s_addr == 0) { if (nodePublicSocket.sin_addr.s_addr == 0) {
inet_aton(_domainIP, &nodePublicSocket.sin_addr); nodePublicSocket.sin_addr.s_addr = htonl(_domainIP.toIPv4Address());
} }
addOrUpdateNode((sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket, nodeType, nodeId); addOrUpdateNode((sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket, nodeType, nodeId);
@ -376,9 +398,9 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte
return readNodes; return readNodes;
} }
const char GLOBAL_ASSIGNMENT_SERVER_HOSTNAME[] = "assignment.highfidelity.io"; const char LOCAL_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost";
const sockaddr_in GLOBAL_ASSIGNMENT_SOCKET = socketForHostnameAndHostOrderPort(GLOBAL_ASSIGNMENT_SERVER_HOSTNAME, const sockaddr_in DEFAULT_LOCAL_ASSIGNMENT_SOCKET = socketForHostnameAndHostOrderPort(LOCAL_ASSIGNMENT_SERVER_HOSTNAME,
ASSIGNMENT_SERVER_PORT); DEFAULT_DOMAIN_SERVER_PORT);
void NodeList::sendAssignment(Assignment& assignment) { void NodeList::sendAssignment(Assignment& assignment) {
unsigned char assignmentPacket[MAX_PACKET_SIZE]; unsigned char assignmentPacket[MAX_PACKET_SIZE];
@ -390,7 +412,7 @@ void NodeList::sendAssignment(Assignment& assignment) {
int numAssignmentBytes = assignment.packToBuffer(assignmentPacket + numHeaderBytes); int numAssignmentBytes = assignment.packToBuffer(assignmentPacket + numHeaderBytes);
sockaddr* assignmentServerSocket = (_assignmentServerSocket == NULL) sockaddr* assignmentServerSocket = (_assignmentServerSocket == NULL)
? (sockaddr*) &GLOBAL_ASSIGNMENT_SOCKET ? (sockaddr*) &DEFAULT_LOCAL_ASSIGNMENT_SOCKET
: _assignmentServerSocket; : _assignmentServerSocket;
_nodeSocket.send(assignmentServerSocket, assignmentPacket, numHeaderBytes + numAssignmentBytes); _nodeSocket.send(assignmentServerSocket, assignmentPacket, numHeaderBytes + numAssignmentBytes);
@ -555,8 +577,7 @@ void NodeList::loadData(QSettings *settings) {
QString domainServerHostname = settings->value(DOMAIN_SERVER_SETTING_KEY).toString(); QString domainServerHostname = settings->value(DOMAIN_SERVER_SETTING_KEY).toString();
if (domainServerHostname.size() > 0) { if (domainServerHostname.size() > 0) {
memset(_domainHostname, 0, MAX_HOSTNAME_BYTES); _domainHostname = domainServerHostname;
memcpy(_domainHostname, domainServerHostname.toLocal8Bit().constData(), domainServerHostname.size());
} }
settings->endGroup(); settings->endGroup();
@ -565,7 +586,7 @@ void NodeList::loadData(QSettings *settings) {
void NodeList::saveData(QSettings* settings) { void NodeList::saveData(QSettings* settings) {
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY); settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
if (memcmp(_domainHostname, DEFAULT_DOMAIN_HOSTNAME, strlen(DEFAULT_DOMAIN_HOSTNAME)) != 0) { if (_domainHostname != DEFAULT_DOMAIN_HOSTNAME) {
// the user is using a different hostname, store it // the user is using a different hostname, store it
settings->setValue(DOMAIN_SERVER_SETTING_KEY, QVariant(_domainHostname)); settings->setValue(DOMAIN_SERVER_SETTING_KEY, QVariant(_domainHostname));
} else { } else {

View file

@ -14,6 +14,7 @@
#include <iterator> #include <iterator>
#include <unistd.h> #include <unistd.h>
#include <QtNetwork/QHostAddress>
#include <QtCore/QSettings> #include <QtCore/QSettings>
#include "Node.h" #include "Node.h"
@ -36,9 +37,8 @@ extern const char SOLO_NODE_TYPES[2];
const int MAX_HOSTNAME_BYTES = 256; const int MAX_HOSTNAME_BYTES = 256;
extern const char DEFAULT_DOMAIN_HOSTNAME[MAX_HOSTNAME_BYTES]; extern const QString DEFAULT_DOMAIN_HOSTNAME;
extern const char DEFAULT_DOMAIN_IP[INET_ADDRSTRLEN]; // IP Address will be re-set by lookup on startup extern const unsigned short DEFAULT_DOMAIN_SERVER_PORT;
extern const int DEFAULT_DOMAINSERVER_PORT;
const int UNKNOWN_NODE_ID = 0; const int UNKNOWN_NODE_ID = 0;
@ -65,15 +65,18 @@ public:
NodeListIterator begin() const; NodeListIterator begin() const;
NodeListIterator end() const; NodeListIterator end() const;
NODE_TYPE getOwnerType() const { return _ownerType; } NODE_TYPE getOwnerType() const { return _ownerType; }
void setOwnerType(NODE_TYPE ownerType) { _ownerType = ownerType; } void setOwnerType(NODE_TYPE ownerType) { _ownerType = ownerType; }
const char* getDomainHostname() const { return _domainHostname; } const QString& getDomainHostname() const { return _domainHostname; }
void setDomainHostname(const char* domainHostname); void setDomainHostname(const QString& domainHostname);
void setDomainIP(const char* domainIP); const QHostAddress& getDomainIP() const { return _domainIP; }
void setDomainIPToLocalhost(); void setDomainIP(const QHostAddress& domainIP) { _domainIP = domainIP; }
void setDomainIPToLocalhost() { _domainIP = QHostAddress(INADDR_LOOPBACK); }
unsigned short getDomainPort() const { return _domainPort; }
void setDomainPort(unsigned short domainPort) { _domainPort = domainPort; }
uint16_t getLastNodeID() const { return _lastNodeID; } uint16_t getLastNodeID() const { return _lastNodeID; }
void increaseNodeID() { (++_lastNodeID == UNKNOWN_NODE_ID) ? ++_lastNodeID : _lastNodeID; } void increaseNodeID() { (++_lastNodeID == UNKNOWN_NODE_ID) ? ++_lastNodeID : _lastNodeID; }
@ -96,7 +99,7 @@ public:
void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest); void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest);
void sendDomainServerCheckIn(); void sendDomainServerCheckIn(const char* assignmentUUID = NULL);
int processDomainServerList(unsigned char *packetData, size_t dataBytes); int processDomainServerList(unsigned char *packetData, size_t dataBytes);
void setAssignmentServerSocket(sockaddr* serverSocket) { _assignmentServerSocket = serverSocket; } void setAssignmentServerSocket(sockaddr* serverSocket) { _assignmentServerSocket = serverSocket; }
@ -140,8 +143,9 @@ private:
void addNodeToList(Node* newNode); void addNodeToList(Node* newNode);
char _domainHostname[MAX_HOSTNAME_BYTES]; QString _domainHostname;
char _domainIP[INET_ADDRSTRLEN]; QHostAddress _domainIP;
unsigned short _domainPort;
Node** _nodeBuckets[MAX_NUM_NODES / NODES_PER_BUCKET]; Node** _nodeBuckets[MAX_NUM_NODES / NODES_PER_BUCKET];
int _numNodes; int _numNodes;
UDPSocket _nodeSocket; UDPSocket _nodeSocket;

View file

@ -26,7 +26,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
return 1; return 1;
case PACKET_TYPE_VOXEL_STATS: case PACKET_TYPE_VOXEL_STATS:
return 2; return 2;
default: default:
return 0; return 0;
} }

View file

@ -58,6 +58,4 @@ const int MAX_PACKET_HEADER_BYTES = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION)
#define ADD_SCENE_COMMAND "add scene" #define ADD_SCENE_COMMAND "add scene"
#define TEST_COMMAND "a message" #define TEST_COMMAND "a message"
const unsigned short ASSIGNMENT_SERVER_PORT = 7007;
#endif #endif

View file

@ -275,7 +275,7 @@ int UDPSocket::send(sockaddr* destAddress, const void* data, size_t byteLength)
return sent_bytes; return sent_bytes;
} }
int UDPSocket::send(char* destAddress, int destPort, const void* data, size_t byteLength) const { 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 // change address and port on reusable global to passed variables
destSockaddr.sin_addr.s_addr = inet_addr(destAddress); destSockaddr.sin_addr.s_addr = inet_addr(destAddress);

View file

@ -31,7 +31,7 @@ public:
void setBlockingReceiveTimeoutInUsecs(int timeoutUsecs); void setBlockingReceiveTimeoutInUsecs(int timeoutUsecs);
int send(sockaddr* destAddress, const void* data, size_t byteLength) const; int send(sockaddr* destAddress, const void* data, size_t byteLength) const;
int send(char* destAddress, int destPort, 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(void* receivedData, ssize_t* receivedBytes) const;
bool receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const; bool receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const;

View file

@ -28,14 +28,17 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; _voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
_voxelPacketAt = _voxelPacket; _voxelPacketAt = _voxelPacket;
resetVoxelPacket(); resetVoxelPacket();
}
void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
// Create voxel sending thread... // Create voxel sending thread...
uint16_t nodeID = getOwningNode()->getNodeID(); uint16_t nodeID = getOwningNode()->getNodeID();
_voxelSendThread = new VoxelSendThread(nodeID); _voxelSendThread = new VoxelSendThread(nodeID, voxelServer);
_voxelSendThread->initialize(true); _voxelSendThread->initialize(true);
} }
void VoxelNodeData::resetVoxelPacket() { void VoxelNodeData::resetVoxelPacket() {
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state. // the clients requested color state.
@ -57,8 +60,10 @@ void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) {
VoxelNodeData::~VoxelNodeData() { VoxelNodeData::~VoxelNodeData() {
delete[] _voxelPacket; delete[] _voxelPacket;
_voxelSendThread->terminate(); if (_voxelSendThread) {
delete _voxelSendThread; _voxelSendThread->terminate();
delete _voxelSendThread;
}
} }
bool VoxelNodeData::updateCurrentViewFrustum() { bool VoxelNodeData::updateCurrentViewFrustum() {

View file

@ -19,6 +19,7 @@
#include <VoxelSceneStats.h> #include <VoxelSceneStats.h>
class VoxelSendThread; class VoxelSendThread;
class VoxelServer;
class VoxelNodeData : public AvatarData { class VoxelNodeData : public AvatarData {
public: public:
@ -65,6 +66,9 @@ public:
VoxelSceneStats stats; VoxelSceneStats stats;
void initializeVoxelSendThread(VoxelServer* voxelServer);
bool isVoxelSendThreadInitalized() { return _voxelSendThread; }
private: private:
VoxelNodeData(const VoxelNodeData &); VoxelNodeData(const VoxelNodeData &);
VoxelNodeData& operator= (const VoxelNodeData&); VoxelNodeData& operator= (const VoxelNodeData&);

View file

@ -16,10 +16,11 @@ extern EnvironmentData environmentData[3];
#include "VoxelSendThread.h" #include "VoxelSendThread.h"
#include "VoxelServer.h" #include "VoxelServer.h"
#include "VoxelServerState.h" #include "VoxelServerConsts.h"
VoxelSendThread::VoxelSendThread(uint16_t nodeID) : VoxelSendThread::VoxelSendThread(uint16_t nodeID, VoxelServer* myServer) :
_nodeID(nodeID) { _nodeID(nodeID),
_myServer(myServer) {
} }
bool VoxelSendThread::process() { bool VoxelSendThread::process() {
@ -35,7 +36,7 @@ bool VoxelSendThread::process() {
// Sometimes the node data has not yet been linked, in which case we can't really do anything // Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData) { if (nodeData) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
} }
deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
@ -47,7 +48,7 @@ bool VoxelSendThread::process() {
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);
} else { } else {
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
std::cout << "Last send took too much time, not sleeping!\n"; std::cout << "Last send took too much time, not sleeping!\n";
} }
} }
@ -94,7 +95,7 @@ void VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
/// Version of voxel distributor that sends the deepest LOD level at once /// Version of voxel distributor that sends the deepest LOD level at once
void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) { void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) {
pthread_mutex_lock(&::treeLock); _myServer->lockTree();
int truePacketsSent = 0; int truePacketsSent = 0;
int trueBytesSent = 0; int trueBytesSent = 0;
@ -113,7 +114,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
if (wantColor != nodeData->getCurrentPacketIsColor()) { if (wantColor != nodeData->getCurrentPacketIsColor()) {
if (nodeData->isPacketWaiting()) { if (nodeData->isPacketWaiting()) {
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
} }
@ -121,7 +122,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
} else { } else {
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
} }
@ -129,7 +130,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
} }
} }
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n", printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
@ -137,7 +138,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
debug::valueOf(nodeData->getViewSent()) debug::valueOf(nodeData->getViewSent())
@ -148,7 +149,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
// the current view frustum for things to send. // the current view frustum for things to send.
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
uint64_t now = usecTimestampNow(); uint64_t now = usecTimestampNow();
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
if (nodeData->getLastTimeBagEmpty() > 0) { if (nodeData->getLastTimeBagEmpty() > 0) {
@ -166,7 +167,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
// if our view has changed, we need to reset these things... // if our view has changed, we need to reset these things...
if (viewFrustumChanged) { if (viewFrustumChanged) {
if (::dumpVoxelsOnMove) { if (_myServer->wantDumpVoxelsOnMove()) {
nodeData->nodeBag.deleteAll(); nodeData->nodeBag.deleteAll();
} }
nodeData->map.erase(); nodeData->map.erase();
@ -180,7 +181,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
nodeData->stats.sceneCompleted(); nodeData->stats.sceneCompleted();
if (::displayVoxelStats) { if (_myServer->wantDisplayVoxelStats()) {
nodeData->stats.printDebugDetails(); nodeData->stats.printDebugDetails();
} }
@ -191,10 +192,10 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
if (isFullScene) { if (isFullScene) {
nodeData->nodeBag.deleteAll(); nodeData->nodeBag.deleteAll();
} }
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getServerTree().rootNode, _myServer->getJurisdiction());
// This is the start of "resending" the scene. // This is the start of "resending" the scene.
nodeData->nodeBag.insert(serverTree.rootNode); nodeData->nodeBag.insert(_myServer->getServerTree().rootNode);
} }
// If we have something in our nodeBag, then turn them into packets and send them out... // If we have something in our nodeBag, then turn them into packets and send them out...
@ -203,8 +204,8 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
int packetsSentThisInterval = 0; int packetsSentThisInterval = 0;
uint64_t start = usecTimestampNow(); uint64_t start = usecTimestampNow();
bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); bool shouldSendEnvironments = _myServer->wantSendEnvironments() && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { while (packetsSentThisInterval < _myServer->getPacketsPerClientPerInterval() - (shouldSendEnvironments ? 1 : 0)) {
// Check to see if we're taking too long, and if so bail early... // Check to see if we're taking too long, and if so bail early...
uint64_t now = usecTimestampNow(); uint64_t now = usecTimestampNow();
long elapsedUsec = (now - start); long elapsedUsec = (now - start);
@ -212,7 +213,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec);
if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) {
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n",
usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket,
nodeData->nodeBag.count()); nodeData->nodeBag.count());
@ -234,10 +235,10 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
wantOcclusionCulling, coverageMap, boundaryLevelAdjust, wantOcclusionCulling, coverageMap, boundaryLevelAdjust,
nodeData->getLastTimeBagEmpty(), nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, ::jurisdiction); isFullScene, &nodeData->stats, _myServer->getJurisdiction());
nodeData->stats.encodeStarted(); nodeData->stats.encodeStarted();
bytesWritten = serverTree.encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1, bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1,
nodeData->nodeBag, params); nodeData->nodeBag, params);
nodeData->stats.encodeStopped(); nodeData->stats.encodeStopped();
@ -254,17 +255,17 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
nodeData->resetVoxelPacket(); nodeData->resetVoxelPacket();
} }
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left packetsSentThisInterval = _myServer->getPacketsPerClientPerInterval(); // done for now, no nodes left
} }
} }
// send the environment packet // send the environment packet
if (shouldSendEnvironments) { if (shouldSendEnvironments) {
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
int envPacketLength = numBytesPacketHeader; int envPacketLength = numBytesPacketHeader;
int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); int environmentsToSend = _myServer->getSendMinimalEnvironment() ? 1 : _myServer->getEnvironmentDataCount();
for (int i = 0; i < environmentsToSend; i++) { for (int i = 0; i < environmentsToSend; i++) {
envPacketLength += environmentData[i].getBroadcastData(_tempOutputBuffer + envPacketLength); envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength);
} }
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), _tempOutputBuffer, envPacketLength); NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), _tempOutputBuffer, envPacketLength);
@ -283,7 +284,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
} }
} else if (::debugVoxelSending) { } else if (_myServer->wantsDebugVoxelSending()) {
printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
} }
@ -293,7 +294,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
if (nodeData->nodeBag.isEmpty()) { if (nodeData->nodeBag.isEmpty()) {
nodeData->updateLastKnownViewFrustum(); nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true); nodeData->setViewSent(true);
if (::debugVoxelSending) { if (_myServer->wantsDebugVoxelSending()) {
nodeData->map.printStats(); nodeData->map.printStats();
} }
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
@ -301,6 +302,6 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
} // end if bag wasn't empty, and so we sent stuff... } // end if bag wasn't empty, and so we sent stuff...
pthread_mutex_unlock(&::treeLock); _myServer->unlockTree();
} }

View file

@ -16,17 +16,19 @@
#include <VoxelTree.h> #include <VoxelTree.h>
#include <VoxelNodeBag.h> #include <VoxelNodeBag.h>
#include "VoxelNodeData.h" #include "VoxelNodeData.h"
#include "VoxelServer.h"
/// Threaded processor for sending voxel packets to a single client /// Threaded processor for sending voxel packets to a single client
class VoxelSendThread : public virtual GenericThread { class VoxelSendThread : public virtual GenericThread {
public: public:
VoxelSendThread(uint16_t nodeID); VoxelSendThread(uint16_t nodeID, VoxelServer* myServer);
protected: protected:
/// Implements generic processing behavior for this thread. /// Implements generic processing behavior for this thread.
virtual bool process(); virtual bool process();
private: private:
uint16_t _nodeID; uint16_t _nodeID;
VoxelServer* _myServer;
void handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent); void handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent);
void deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged); void deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged);

View file

@ -11,10 +11,13 @@
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <QDebug>
#include <QString>
#include <QStringList>
#include <OctalCode.h> #include <OctalCode.h>
#include <NodeList.h> #include <NodeList.h>
#include <NodeTypes.h> #include <NodeTypes.h>
#include <EnvironmentData.h>
#include <VoxelTree.h> #include <VoxelTree.h>
#include "VoxelNodeData.h" #include "VoxelNodeData.h"
#include <SharedUtil.h> #include <SharedUtil.h>
@ -23,11 +26,6 @@
#include <PerfStat.h> #include <PerfStat.h>
#include <JurisdictionSender.h> #include <JurisdictionSender.h>
#include "NodeWatcher.h"
#include "VoxelPersistThread.h"
#include "VoxelSendThread.h"
#include "VoxelServerPacketProcessor.h"
#ifdef _WIN32 #ifdef _WIN32
#include "Syssocket.h" #include "Syssocket.h"
#include "Systime.h" #include "Systime.h"
@ -38,34 +36,10 @@
#endif #endif
#include "VoxelServer.h" #include "VoxelServer.h"
#include "VoxelServerState.h" #include "VoxelServerConsts.h"
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/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) { void attachVoxelNodeDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) { if (newNode->getLinkedData() == NULL) {
@ -73,19 +47,134 @@ void attachVoxelNodeDataToNode(Node* newNode) {
} }
} }
VoxelServer::VoxelServer(Assignment::Command command, Assignment::Location location) :
Assignment(command, Assignment::VoxelServerType, location),
_serverTree(true) {
_argc = 0;
_argv = NULL;
_dontKillOnMissingDomain = false;
_packetsPerClientPerInterval = 10;
_wantVoxelPersist = true;
_wantLocalDomain = false;
_debugVoxelSending = false;
_shouldShowAnimationDebug = false;
_displayVoxelStats = false;
_debugVoxelReceiving = false;
_sendEnvironments = true;
_sendMinimalEnvironment = false;
_dumpVoxelsOnMove = false;
_jurisdiction = NULL;
_jurisdictionSender = NULL;
_voxelServerPacketProcessor = NULL;
_voxelPersistThread = NULL;
_parsedArgV = NULL;
}
VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes),
_serverTree(true) {
_argc = 0;
_argv = NULL;
_dontKillOnMissingDomain = false;
_packetsPerClientPerInterval = 10;
_wantVoxelPersist = true;
_wantLocalDomain = false;
_debugVoxelSending = false;
_shouldShowAnimationDebug = false;
_displayVoxelStats = false;
_debugVoxelReceiving = false;
_sendEnvironments = true;
_sendMinimalEnvironment = false;
_dumpVoxelsOnMove = false;
_jurisdiction = NULL;
_jurisdictionSender = NULL;
_voxelServerPacketProcessor = NULL;
_voxelPersistThread = NULL;
_parsedArgV = NULL;
}
VoxelServer::~VoxelServer() {
if (_parsedArgV) {
for (int i = 0; i < _argc; i++) {
delete[] _parsedArgV[i];
}
delete[] _parsedArgV;
}
}
void VoxelServer::setArguments(int argc, char** argv) { void VoxelServer::setArguments(int argc, char** argv) {
_argc = argc; _argc = argc;
_argv = const_cast<const char**>(argv); _argv = const_cast<const char**>(argv);
qDebug("VoxelServer::setArguments()\n");
for (int i = 0; i < _argc; i++) {
qDebug("_argv[%d]=%s\n", i, _argv[i]);
}
} }
void VoxelServer::setupDomainAndPort(const char* domain, int port) { void VoxelServer::parsePayload() {
if (getNumPayloadBytes() > 0) {
QString multiConfig((const char*)getPayload());
QStringList multiConfigList = multiConfig.split(";");
// There there are multiple configs, then this instance will run the first
// config, and launch Assignment requests for the additional configs.
if (multiConfigList.size() > 1) {
qDebug("Voxel Server received assignment for multiple Configs... config count=%d\n", multiConfigList.size());
// skip 0 - that's the one we'll run
for (int i = 1; i < multiConfigList.size(); i++) {
QString config = multiConfigList.at(i);
qDebug(" config[%d]=%s\n", i, config.toLocal8Bit().constData());
Assignment voxelServerAssignment(Assignment::CreateCommand,
Assignment::VoxelServerType,
getLocation()); // use same location as we were created in.
int payloadLength = config.length() + sizeof(char);
voxelServerAssignment.setPayload((uchar*)config.toLocal8Bit().constData(), payloadLength);
qDebug("Requesting additional Voxel Server assignment to handle config %d\n", i);
NodeList::getInstance()->sendAssignment(voxelServerAssignment);
}
}
// Now, parse the first config
QString config = multiConfigList.at(0);
QStringList configList = config.split(" ");
int argCount = configList.size() + 1;
qDebug("VoxelServer::parsePayload()... argCount=%d\n",argCount);
_parsedArgV = new char*[argCount];
const char* dummy = "config-from-payload";
_parsedArgV[0] = new char[strlen(dummy) + sizeof(char)];
strcpy(_parsedArgV[0], dummy);
for (int i = 1; i < argCount; i++) {
QString configItem = configList.at(i-1);
_parsedArgV[i] = new char[configItem.length() + sizeof(char)];
strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData());
qDebug("VoxelServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]);
}
setArguments(argCount, _parsedArgV);
}
}
void VoxelServer::setupStandAlone(const char* domain, int port) {
NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, port); NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, port);
// Handle Local Domain testing with the --local command line // Handle Local Domain testing with the --local command line
const char* local = "--local"; const char* local = "--local";
::wantLocalDomain = strcmp(domain, local) == 0; _wantLocalDomain = strcmp(domain, local) == 0;
if (::wantLocalDomain) { if (_wantLocalDomain) {
printf("Local Domain MODE!\n"); qDebug("Local Domain MODE!\n");
NodeList::getInstance()->setDomainIPToLocalhost(); NodeList::getInstance()->setDomainIPToLocalhost();
} else { } else {
if (domain) { if (domain) {
@ -99,54 +188,60 @@ void VoxelServer::setupDomainAndPort(const char* domain, int port) {
//int main(int argc, const char * argv[]) { //int main(int argc, const char * argv[]) {
void VoxelServer::run() { void VoxelServer::run() {
pthread_mutex_init(&::treeLock, NULL);
// Now would be a good time to parse our arguments, if we got them as assignment
if (getNumPayloadBytes() > 0) {
parsePayload();
}
pthread_mutex_init(&_treeLock, NULL);
qInstallMessageHandler(sharedMessageHandler); qInstallMessageHandler(sharedMessageHandler);
const char* JURISDICTION_FILE = "--jurisdictionFile"; const char* JURISDICTION_FILE = "--jurisdictionFile";
const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE); const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE);
if (jurisdictionFile) { if (jurisdictionFile) {
printf("jurisdictionFile=%s\n", jurisdictionFile); qDebug("jurisdictionFile=%s\n", jurisdictionFile);
printf("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); qDebug("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
jurisdiction = new JurisdictionMap(jurisdictionFile); _jurisdiction = new JurisdictionMap(jurisdictionFile);
printf("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); qDebug("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
} else { } else {
const char* JURISDICTION_ROOT = "--jurisdictionRoot"; const char* JURISDICTION_ROOT = "--jurisdictionRoot";
const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT); const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT);
if (jurisdictionRoot) { if (jurisdictionRoot) {
printf("jurisdictionRoot=%s\n", jurisdictionRoot); qDebug("jurisdictionRoot=%s\n", jurisdictionRoot);
} }
const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes"; const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes";
const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES); const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES);
if (jurisdictionEndNodes) { if (jurisdictionEndNodes) {
printf("jurisdictionEndNodes=%s\n", jurisdictionEndNodes); qDebug("jurisdictionEndNodes=%s\n", jurisdictionEndNodes);
} }
if (jurisdictionRoot || jurisdictionEndNodes) { if (jurisdictionRoot || jurisdictionEndNodes) {
::jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); _jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes);
} }
} }
// should we send environments? Default is yes, but this command line suppresses sending // should we send environments? Default is yes, but this command line suppresses sending
const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove"; const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove";
::dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE); _dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE);
printf("dumpVoxelsOnMove=%s\n", debug::valueOf(::dumpVoxelsOnMove)); qDebug("dumpVoxelsOnMove=%s\n", debug::valueOf(_dumpVoxelsOnMove));
// should we send environments? Default is yes, but this command line suppresses sending // should we send environments? Default is yes, but this command line suppresses sending
const char* DONT_SEND_ENVIRONMENTS = "--dontSendEnvironments"; const char* DONT_SEND_ENVIRONMENTS = "--dontSendEnvironments";
bool dontSendEnvironments = getCmdOption(_argc, _argv, DONT_SEND_ENVIRONMENTS); bool dontSendEnvironments = getCmdOption(_argc, _argv, DONT_SEND_ENVIRONMENTS);
if (dontSendEnvironments) { if (dontSendEnvironments) {
printf("Sending environments suppressed...\n"); qDebug("Sending environments suppressed...\n");
::sendEnvironments = false; _sendEnvironments = false;
} else { } else {
// should we send environments? Default is yes, but this command line suppresses sending // should we send environments? Default is yes, but this command line suppresses sending
const char* MINIMAL_ENVIRONMENT = "--MinimalEnvironment"; const char* MINIMAL_ENVIRONMENT = "--MinimalEnvironment";
::sendMinimalEnvironment = getCmdOption(_argc, _argv, MINIMAL_ENVIRONMENT); _sendMinimalEnvironment = getCmdOption(_argc, _argv, MINIMAL_ENVIRONMENT);
printf("Using Minimal Environment=%s\n", debug::valueOf(::sendMinimalEnvironment)); qDebug("Using Minimal Environment=%s\n", debug::valueOf(_sendMinimalEnvironment));
} }
printf("Sending environments=%s\n", debug::valueOf(::sendEnvironments)); qDebug("Sending environments=%s\n", debug::valueOf(_sendEnvironments));
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER); nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER);
@ -154,72 +249,72 @@ void VoxelServer::run() {
setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0);
// tell our NodeList about our desire to get notifications // tell our NodeList about our desire to get notifications
nodeList->addHook(&nodeWatcher); nodeList->addHook(&_nodeWatcher);
nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode; nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode;
nodeList->startSilentNodeRemovalThread(); nodeList->startSilentNodeRemovalThread();
srand((unsigned)time(0)); srand((unsigned)time(0));
const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats"; const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats";
::displayVoxelStats = getCmdOption(_argc, _argv, DISPLAY_VOXEL_STATS); _displayVoxelStats = getCmdOption(_argc, _argv, DISPLAY_VOXEL_STATS);
printf("displayVoxelStats=%s\n", debug::valueOf(::displayVoxelStats)); qDebug("displayVoxelStats=%s\n", debug::valueOf(_displayVoxelStats));
const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending"; const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending";
::debugVoxelSending = getCmdOption(_argc, _argv, DEBUG_VOXEL_SENDING); _debugVoxelSending = getCmdOption(_argc, _argv, DEBUG_VOXEL_SENDING);
printf("debugVoxelSending=%s\n", debug::valueOf(::debugVoxelSending)); qDebug("debugVoxelSending=%s\n", debug::valueOf(_debugVoxelSending));
const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving"; const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving";
::debugVoxelReceiving = getCmdOption(_argc, _argv, DEBUG_VOXEL_RECEIVING); _debugVoxelReceiving = getCmdOption(_argc, _argv, DEBUG_VOXEL_RECEIVING);
printf("debugVoxelReceiving=%s\n", debug::valueOf(::debugVoxelReceiving)); qDebug("debugVoxelReceiving=%s\n", debug::valueOf(_debugVoxelReceiving));
const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug"; const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug";
::shouldShowAnimationDebug = getCmdOption(_argc, _argv, WANT_ANIMATION_DEBUG); _shouldShowAnimationDebug = getCmdOption(_argc, _argv, WANT_ANIMATION_DEBUG);
printf("shouldShowAnimationDebug=%s\n", debug::valueOf(::shouldShowAnimationDebug)); qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug));
// By default we will voxel persist, if you want to disable this, then pass in this parameter // By default we will voxel persist, if you want to disable this, then pass in this parameter
const char* NO_VOXEL_PERSIST = "--NoVoxelPersist"; const char* NO_VOXEL_PERSIST = "--NoVoxelPersist";
if ( getCmdOption(_argc, _argv, NO_VOXEL_PERSIST)) { if (getCmdOption(_argc, _argv, NO_VOXEL_PERSIST)) {
::wantVoxelPersist = false; _wantVoxelPersist = false;
} }
printf("wantVoxelPersist=%s\n", debug::valueOf(::wantVoxelPersist)); qDebug("wantVoxelPersist=%s\n", debug::valueOf(_wantVoxelPersist));
// if we want Voxel Persistence, load the local file now... // if we want Voxel Persistence, load the local file now...
bool persistantFileRead = false; bool persistantFileRead = false;
if (::wantVoxelPersist) { if (_wantVoxelPersist) {
// Check to see if the user passed in a command line option for setting packet send rate // 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* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename";
const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME); const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME);
if (voxelsPersistFilenameParameter) { if (voxelsPersistFilenameParameter) {
strcpy(voxelPersistFilename, voxelsPersistFilenameParameter); strcpy(_voxelPersistFilename, voxelsPersistFilenameParameter);
} else { } else {
//strcpy(voxelPersistFilename, ::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); //strcpy(voxelPersistFilename, _wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
strcpy(voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE); strcpy(_voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE);
} }
printf("loading voxels from file: %s...\n", voxelPersistFilename); qDebug("loading voxels from file: %s...\n", _voxelPersistFilename);
persistantFileRead = ::serverTree.readFromSVOFile(::voxelPersistFilename); persistantFileRead = _serverTree.readFromSVOFile(_voxelPersistFilename);
if (persistantFileRead) { if (persistantFileRead) {
PerformanceWarning warn(::shouldShowAnimationDebug, PerformanceWarning warn(_shouldShowAnimationDebug,
"persistVoxelsWhenDirty() - reaverageVoxelColors()", ::shouldShowAnimationDebug); "persistVoxelsWhenDirty() - reaverageVoxelColors()", _shouldShowAnimationDebug);
// after done inserting all these voxels, then reaverage colors // after done inserting all these voxels, then reaverage colors
serverTree.reaverageVoxelColors(serverTree.rootNode); _serverTree.reaverageVoxelColors(_serverTree.rootNode);
printf("Voxels reAveraged\n"); qDebug("Voxels reAveraged\n");
} }
::serverTree.clearDirtyBit(); // the tree is clean since we just loaded it _serverTree.clearDirtyBit(); // the tree is clean since we just loaded it
printf("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
unsigned long nodeCount = ::serverTree.rootNode->getSubTreeNodeCount(); unsigned long nodeCount = _serverTree.rootNode->getSubTreeNodeCount();
unsigned long internalNodeCount = ::serverTree.rootNode->getSubTreeInternalNodeCount(); unsigned long internalNodeCount = _serverTree.rootNode->getSubTreeInternalNodeCount();
unsigned long leafNodeCount = ::serverTree.rootNode->getSubTreeLeafNodeCount(); unsigned long leafNodeCount = _serverTree.rootNode->getSubTreeLeafNodeCount();
printf("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount);
// now set up VoxelPersistThread // now set up VoxelPersistThread
::voxelPersistThread = new VoxelPersistThread(&::serverTree, ::voxelPersistFilename); _voxelPersistThread = new VoxelPersistThread(&_serverTree, _voxelPersistFilename);
if (::voxelPersistThread) { if (_voxelPersistThread) {
::voxelPersistThread->initialize(true); _voxelPersistThread->initialize(true);
} }
} }
@ -228,52 +323,52 @@ void VoxelServer::run() {
const char* INPUT_FILE = "-i"; const char* INPUT_FILE = "-i";
const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE); const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE);
if (voxelsFilename) { if (voxelsFilename) {
serverTree.readFromSVOFile(voxelsFilename); _serverTree.readFromSVOFile(voxelsFilename);
} }
// Check to see if the user passed in a command line option for setting packet send rate // 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* PACKETS_PER_SECOND = "--packetsPerSecond";
const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND); const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND);
if (packetsPerSecond) { if (packetsPerSecond) {
PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/INTERVALS_PER_SECOND; _packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND;
if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) { if (_packetsPerClientPerInterval < 1) {
PACKETS_PER_CLIENT_PER_INTERVAL = 1; _packetsPerClientPerInterval = 1;
} }
printf("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, PACKETS_PER_CLIENT_PER_INTERVAL); qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval);
} }
// for now, initialize the environments with fixed values // for now, initialize the environments with fixed values
environmentData[1].setID(1); _environmentData[1].setID(1);
environmentData[1].setGravity(1.0f); _environmentData[1].setGravity(1.0f);
environmentData[1].setAtmosphereCenter(glm::vec3(0.5, 0.5, (0.25 - 0.06125)) * (float)TREE_SCALE); _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].setAtmosphereInnerRadius(0.030625f * TREE_SCALE);
environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f); _environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f);
environmentData[2].setID(2); _environmentData[2].setID(2);
environmentData[2].setGravity(1.0f); _environmentData[2].setGravity(1.0f);
environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE); _environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE);
environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE); _environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE);
environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); _environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f);
environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue _environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue
sockaddr senderAddress; sockaddr senderAddress;
unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t packetLength; ssize_t packetLength;
timeval lastDomainServerCheckIn = {}; timeval lastDomainServerCheckIn = {};
// set up our jurisdiction broadcaster... // set up our jurisdiction broadcaster...
::jurisdictionSender = new JurisdictionSender(::jurisdiction); _jurisdictionSender = new JurisdictionSender(_jurisdiction);
if (::jurisdictionSender) { if (_jurisdictionSender) {
::jurisdictionSender->initialize(true); _jurisdictionSender->initialize(true);
} }
// set up our VoxelServerPacketProcessor // set up our VoxelServerPacketProcessor
::voxelServerPacketProcessor = new VoxelServerPacketProcessor(); _voxelServerPacketProcessor = new VoxelServerPacketProcessor(this);
if (::voxelServerPacketProcessor) { if (_voxelServerPacketProcessor) {
::voxelServerPacketProcessor->initialize(true); _voxelServerPacketProcessor->initialize(true);
} }
// loop to send to nodes requesting data // loop to send to nodes requesting data
while (true) { while (true) {
@ -304,46 +399,50 @@ void VoxelServer::run() {
nodeID); nodeID);
NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength); NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength);
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
nodeData->initializeVoxelSendThread(this);
}
} else if (packetData[0] == PACKET_TYPE_PING) { } else if (packetData[0] == PACKET_TYPE_PING) {
// If the packet is a ping, let processNodeData handle it. // If the packet is a ping, let processNodeData handle it.
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
} else if (packetData[0] == PACKET_TYPE_DOMAIN) { } else if (packetData[0] == PACKET_TYPE_DOMAIN) {
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
} else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { } else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
if (::jurisdictionSender) { if (_jurisdictionSender) {
::jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength); _jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
} }
} else if (::voxelServerPacketProcessor) { } else if (_voxelServerPacketProcessor) {
::voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength); _voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
} else { } else {
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]); qDebug("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
} }
} }
} }
if (::jurisdiction) { delete _jurisdiction;
delete ::jurisdiction;
}
if (::jurisdictionSender) { if (_jurisdictionSender) {
::jurisdictionSender->terminate(); _jurisdictionSender->terminate();
delete ::jurisdictionSender; delete _jurisdictionSender;
} }
if (::voxelServerPacketProcessor) { if (_voxelServerPacketProcessor) {
::voxelServerPacketProcessor->terminate(); _voxelServerPacketProcessor->terminate();
delete ::voxelServerPacketProcessor; delete _voxelServerPacketProcessor;
} }
if (::voxelPersistThread) { if (_voxelPersistThread) {
::voxelPersistThread->terminate(); _voxelPersistThread->terminate();
delete ::voxelPersistThread; delete _voxelPersistThread;
} }
// tell our NodeList we're done with notifications // tell our NodeList we're done with notifications
nodeList->removeHook(&nodeWatcher); nodeList->removeHook(&_nodeWatcher);
pthread_mutex_destroy(&::treeLock); pthread_mutex_destroy(&_treeLock);
} }

View file

@ -1,3 +1,4 @@
//
// VoxelServer.h // VoxelServer.h
// voxel-server // voxel-server
// //
@ -9,28 +10,84 @@
#ifndef __voxel_server__VoxelServer__ #ifndef __voxel_server__VoxelServer__
#define __voxel_server__VoxelServer__ #define __voxel_server__VoxelServer__
#include <Assignment.h>
#include <EnvironmentData.h>
#include "NodeWatcher.h"
#include "VoxelPersistThread.h"
#include "VoxelSendThread.h"
#include "VoxelServerConsts.h"
#include "VoxelServerPacketProcessor.h"
/// Handles assignments of type VoxelServer - sending voxels to various clients. /// Handles assignments of type VoxelServer - sending voxels to various clients.
class VoxelServer { class VoxelServer : public Assignment {
public: public:
VoxelServer(Assignment::Command command,
Assignment::Location location = Assignment::GlobalLocation);
VoxelServer(const unsigned char* dataBuffer, int numBytes);
~VoxelServer();
/// runs the voxel server assignment /// runs the voxel server assignment
static void run(); void run();
/// allows setting of run arguments /// allows setting of run arguments
static void setArguments(int argc, char** argv); void setArguments(int argc, char** argv);
/// when VoxelServer class is used by voxel-server stand alone executable it calls this to specify the domain /// 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 /// and port it is handling. When called by assignment-client, this is not needed because assignment-client
/// handles ports and domains automatically. /// 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 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 /// \param int port port the voxel server will listen on
static void setupDomainAndPort(const char* domain, int port); void setupStandAlone(const char* domain, int port);
bool wantsDebugVoxelSending() const { return _debugVoxelSending; }
bool wantsDebugVoxelReceiving() const { return _debugVoxelReceiving; }
bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; }
bool wantSendEnvironments() const { return _sendEnvironments; }
bool wantDumpVoxelsOnMove() const { return _dumpVoxelsOnMove; }
bool wantDisplayVoxelStats() const { return _displayVoxelStats; }
VoxelTree& getServerTree() { return _serverTree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
void lockTree() { pthread_mutex_lock(&_treeLock); }
void unlockTree() { pthread_mutex_unlock(&_treeLock); }
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; }
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); }
private: private:
static int _argc; int _argc;
static const char** _argv; const char** _argv;
static bool _dontKillOnMissingDomain; char** _parsedArgV;
bool _dontKillOnMissingDomain;
char _voxelPersistFilename[MAX_FILENAME_LENGTH];
int _packetsPerClientPerInterval;
VoxelTree _serverTree; // this IS a reaveraging tree
bool _wantVoxelPersist;
bool _wantLocalDomain;
bool _debugVoxelSending;
bool _shouldShowAnimationDebug;
bool _displayVoxelStats;
bool _debugVoxelReceiving;
bool _sendEnvironments;
bool _sendMinimalEnvironment;
bool _dumpVoxelsOnMove;
JurisdictionMap* _jurisdiction;
JurisdictionSender* _jurisdictionSender;
VoxelServerPacketProcessor* _voxelServerPacketProcessor;
VoxelPersistThread* _voxelPersistThread;
pthread_mutex_t _treeLock;
EnvironmentData _environmentData[3];
NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed
void parsePayload();
}; };
#endif // __voxel_server__VoxelServer__ #endif // __voxel_server__VoxelServer__

View file

@ -0,0 +1,31 @@
// VoxelServerConsts.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __voxel_server__VoxelServerConsts__
#define __voxel_server__VoxelServerConsts__
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
#include <VoxelTree.h>
#include "VoxelServerPacketProcessor.h"
const int MAX_FILENAME_LENGTH = 1024;
const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float));
const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES;
const int VOXEL_SEND_INTERVAL_USECS = 17 * 1000; // approximately 60fps
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
const int INTERVALS_PER_SECOND = 1000 * 1000 / VOXEL_SEND_INTERVAL_USECS;
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
extern const char* LOCAL_VOXELS_PERSIST_FILE;
extern const char* VOXELS_PERSIST_FILE;
#endif // __voxel_server__VoxelServerConsts__

View file

@ -12,33 +12,39 @@
#include <PerfStat.h> #include <PerfStat.h>
#include "VoxelServer.h" #include "VoxelServer.h"
#include "VoxelServerState.h" #include "VoxelServerConsts.h"
#include "VoxelServerPacketProcessor.h" #include "VoxelServerPacketProcessor.h"
VoxelServerPacketProcessor::VoxelServerPacketProcessor(VoxelServer* myServer) :
_myServer(myServer),
_receivedPacketCount(0) {
}
void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
int numBytesPacketHeader = numBytesForPacketHeader(packetData); int numBytesPacketHeader = numBytesForPacketHeader(packetData);
if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) { if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) {
bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE); bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
PerformanceWarning warn(::shouldShowAnimationDebug, PerformanceWarning warn(_myServer->wantShowAnimationDebug(),
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
::shouldShowAnimationDebug); _myServer->wantShowAnimationDebug());
::receivedPacketCount++; _receivedPacketCount++;
unsigned short int itemNumber = (*((unsigned short int*)(packetData + numBytesPacketHeader))); unsigned short int itemNumber = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
if (::shouldShowAnimationDebug) { if (_myServer->wantShowAnimationDebug()) {
printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n", printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
packetLength, itemNumber); packetLength, itemNumber);
} }
if (::debugVoxelReceiving) { if (_myServer->wantsDebugVoxelReceiving()) {
printf("got %s - %d command from client receivedBytes=%ld itemNumber=%d\n", printf("got %s - %d command from client receivedBytes=%ld itemNumber=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
::receivedPacketCount, packetLength, itemNumber); _receivedPacketCount, packetLength, itemNumber);
} }
int atByte = numBytesPacketHeader + sizeof(itemNumber); int atByte = numBytesPacketHeader + sizeof(itemNumber);
unsigned char* voxelData = (unsigned char*)&packetData[atByte]; unsigned char* voxelData = (unsigned char*)&packetData[atByte];
@ -48,7 +54,7 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES; int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
int voxelCodeSize = bytesRequiredForCodeLength(octets); int voxelCodeSize = bytesRequiredForCodeLength(octets);
if (::shouldShowAnimationDebug) { if (_myServer->wantShowAnimationDebug()) {
int red = voxelData[voxelCodeSize + 0]; int red = voxelData[voxelCodeSize + 0];
int green = voxelData[voxelCodeSize + 1]; int green = voxelData[voxelCodeSize + 1];
int blue = voxelData[voxelCodeSize + 2]; int blue = voxelData[voxelCodeSize + 2];
@ -58,7 +64,7 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
delete[] vertices; delete[] vertices;
} }
serverTree.readCodeColorBufferToTree(voxelData, destructive); _myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive);
// skip to next // skip to next
voxelData += voxelDataSize; voxelData += voxelDataSize;
atByte += voxelDataSize; atByte += voxelDataSize;
@ -73,9 +79,9 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
} else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) { } else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
// Send these bits off to the VoxelTree class to process them // Send these bits off to the VoxelTree class to process them
pthread_mutex_lock(&::treeLock); _myServer->lockTree();
::serverTree.processRemoveVoxelBitstream((unsigned char*)packetData, packetLength); _myServer->getServerTree().processRemoveVoxelBitstream((unsigned char*)packetData, packetLength);
pthread_mutex_unlock(&::treeLock); _myServer->unlockTree();
// Make sure our Node and NodeList knows we've heard from this node. // Make sure our Node and NodeList knows we've heard from this node.
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);

View file

@ -12,11 +12,20 @@
#define __voxel_server__VoxelServerPacketProcessor__ #define __voxel_server__VoxelServerPacketProcessor__
#include <ReceivedPacketProcessor.h> #include <ReceivedPacketProcessor.h>
class VoxelServer;
/// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes /// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
class VoxelServerPacketProcessor : public ReceivedPacketProcessor { class VoxelServerPacketProcessor : public ReceivedPacketProcessor {
public:
VoxelServerPacketProcessor(VoxelServer* myServer);
protected: protected:
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
private:
VoxelServer* _myServer;
int _receivedPacketCount;
}; };
#endif // __voxel_server__VoxelServerPacketProcessor__ #endif // __voxel_server__VoxelServerPacketProcessor__

View file

@ -1,53 +0,0 @@
// 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__VoxelServerState__
#define __voxel_server__VoxelServerState__
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
#include <VoxelTree.h>
#include "VoxelServerPacketProcessor.h"
const int MAX_FILENAME_LENGTH = 1024;
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;
const float DEATH_STAR_RADIUS = 4.0;
const float MAX_CUBE = 0.05f;
const int VOXEL_SEND_INTERVAL_USECS = 17 * 1000; // approximately 60fps
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
const int INTERVALS_PER_SECOND = 1000 * 1000 / VOXEL_SEND_INTERVAL_USECS;
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
extern const char* LOCAL_VOXELS_PERSIST_FILE;
extern const char* VOXELS_PERSIST_FILE;
extern char voxelPersistFilename[MAX_FILENAME_LENGTH];
extern int PACKETS_PER_CLIENT_PER_INTERVAL;
extern VoxelTree serverTree; // this IS a reaveraging tree
extern bool wantVoxelPersist;
extern bool wantLocalDomain;
extern bool debugVoxelSending;
extern bool shouldShowAnimationDebug;
extern bool displayVoxelStats;
extern bool debugVoxelReceiving;
extern bool sendEnvironments;
extern bool sendMinimalEnvironment;
extern bool dumpVoxelsOnMove;
extern int receivedPacketCount;
extern JurisdictionMap* jurisdiction;
extern JurisdictionSender* jurisdictionSender;
extern VoxelServerPacketProcessor* voxelServerPacketProcessor;
extern pthread_mutex_t treeLock;
#endif // __voxel_server__VoxelServerState__

View file

@ -29,17 +29,19 @@ int main(int argc, const char * argv[]) {
} }
printf("portParameter=%s listenPort=%d\n", portParameter, listenPort); printf("portParameter=%s listenPort=%d\n", portParameter, listenPort);
} }
VoxelServer ourVoxelServer(Assignment::CreateCommand);
if (wantLocalDomain) { if (wantLocalDomain) {
VoxelServer::setupDomainAndPort(local, listenPort); ourVoxelServer.setupStandAlone(local, listenPort);
} else { } else {
if (domainIP) { if (domainIP) {
VoxelServer::setupDomainAndPort(domainIP, listenPort); ourVoxelServer.setupStandAlone(domainIP, listenPort);
} }
} }
VoxelServer::setArguments(argc, const_cast<char**>(argv)); ourVoxelServer.setArguments(argc, const_cast<char**>(argv));
VoxelServer::run(); ourVoxelServer.run();
} }