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(assignment-client)
add_subdirectory(assignment-server)
add_subdirectory(domain-server)
add_subdirectory(eve)
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
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.
@ -106,7 +106,7 @@ window, change directory into the build directory, make the needed components, a
First we make the targets we'll need.
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
@ -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.
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
./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.
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:
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.
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
server computer in the "Domain" edit control.
the Interface menu on OS X or the File menu on Linux, and enter "localhost" for the
server hostname in the "Domain" edit control.
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();
}
const char* domainIP = getCmdOption(argc, argv, "--domain");
if (domainIP) {
NodeList::getInstance()->setDomainIP(domainIP);
const char* domainHostname = getCmdOption(argc, argv, "--domain");
if (domainHostname) {
NodeList::getInstance()->setDomainHostname(domainHostname);
}
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(audio ${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 <QtNetwork/QtNetwork>
#include <AvatarData.h>
#include <NodeList.h>
#include "AvatarData.h"
#include "Agent.h"
#include "voxels/VoxelScriptingInterface.h"
Agent::Agent() :
_shouldStop(false)
{
Agent::Agent(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
}
void Agent::run(QUrl scriptURL) {
void Agent::run() {
NodeList::getInstance()->setOwnerType(NODE_TYPE_AGENT);
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_AVATAR_MIXER, 1);
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1);
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";
QNetworkReply* reply = manager.get(QNetworkRequest(scriptURL));
@ -39,14 +43,13 @@ void Agent::run(QUrl scriptURL) {
QScriptEngine engine;
AvatarData *testAvatarData = new AvatarData;
QScriptValue avatarDataValue = engine.newQObject(testAvatarData);
engine.globalObject().setProperty("Avatar", avatarDataValue);
QScriptValue agentValue = engine.newQObject(this);
engine.globalObject().setProperty("Agent", agentValue);
VoxelScriptingInterface voxelScripter;
QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter);
engine.globalObject().setProperty("Voxels", voxelScripterValue);
qDebug() << "Downloaded script:" << scriptString << "\n";
qDebug() << "Evaluated script:" << engine.evaluate(scriptString).toString() << "\n";
@ -75,9 +78,10 @@ void Agent::run(QUrl scriptURL) {
NodeList::getInstance()->sendDomainServerCheckIn();
}
// allow the scripter's call back to setup visual data
emit preSendCallback();
testAvatarData->sendData();
// flush the voxel packet queue
voxelScripter.getVoxelPacketSender()->process();
if (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) {
NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes);

View file

@ -9,19 +9,19 @@
#ifndef __hifi__Agent__
#define __hifi__Agent__
#include "SharedUtil.h"
#include <QtCore/QObject>
#include <QtCore/QUrl>
class Agent : public QObject {
#include <Assignment.h>
class Agent : public Assignment {
Q_OBJECT
public:
Agent();
Agent(const unsigned char* dataBuffer, int numBytes);
bool volatile _shouldStop;
void run(QUrl scriptUrl);
void run();
signals:
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() {
// change the logging target name while this is running
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
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL);
NodeList::getInstance()->sendDomainServerCheckIn();
NodeList::getInstance()->sendDomainServerCheckIn(this->getUUID().toRfc4122().constData());
if (Logging::shouldSendStats() && numStatCollections > 0) {
// if we should be sending stats to Logstash send the appropriate average now
@ -421,7 +425,7 @@ void AudioMixer::run() {
if (usecToSleep > 0) {
usleep(usecToSleep);
} 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__
#define __hifi__AudioMixer__
#include <Assignment.h>
/// Handles assignments of type AudioMixer - mixing streams of audio and re-distributing to various clients.
class AudioMixer {
class AudioMixer : public Assignment {
public:
AudioMixer(const unsigned char* dataBuffer, int numBytes);
/// runs the audio mixer
static void run();
void run();
};
#endif /* defined(__hifi__AudioMixer__) */

View file

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

View file

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

View file

@ -14,16 +14,19 @@
#include <QtCore/QCoreApplication>
#include "Agent.h"
#include <Assignment.h>
#include <AudioMixer.h>
#include <AvatarMixer.h>
#include <Logging.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.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 char PARENT_TARGET_NAME[] = "assignment-client-monitor";
const char CHILD_TARGET_NAME[] = "assignment-client";
@ -31,6 +34,7 @@ const char CHILD_TARGET_NAME[] = "assignment-client";
pid_t* childForks = NULL;
sockaddr_in customAssignmentSocket = {};
int numForks = 0;
Assignment::Type overiddenAssignmentType = Assignment::AllTypes;
void childClient() {
// 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 = {};
// create a request assignment, accept all assignments, pass the desired pool (if it exists)
Assignment requestAssignment(Assignment::RequestCommand, Assignment::AllTypes);
// create a request assignment, accept assignments defined by the overidden type
Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType);
while (true) {
if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) {
@ -72,51 +76,40 @@ void childClient() {
&& packetVersionMatch(packetData)) {
// 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
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) {
// the domain server IP address is the address we got this packet from
domainSocketAddr = senderSocket.sin_addr;
domainSocket = (sockaddr*) &senderSocket;
} else {
// 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) {
AudioMixer::run();
} else if (deployedAssignment.getType() == Assignment::AvatarMixerType) {
AvatarMixer::run();
} else if (deployedAssignment.getType() == Assignment::VoxelServerType) {
VoxelServer::run();
} else {
// figure out the URL for the script for this agent assignment
QString scriptURLString("http://%1:8080/assignment/%2");
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));
}
// run the deployed assignment
deployedAssignment->run();
} else {
qDebug("Received a bad destination socket for assignment.\n");
}
qDebug("Assignment finished or never started - waiting for new assignment\n");
// delete the deployedAssignment
delete deployedAssignment;
// reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
nodeList->clear();
@ -161,6 +154,7 @@ void sigchldHandler(int sig) {
}
}
}
}
void parentMonitor() {
@ -206,11 +200,20 @@ int main(int argc, const char* argv[]) {
if (customAssignmentServerHostname) {
const char* customAssignmentServerPortString = getCmdOption(argc, argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION);
unsigned short assignmentServerPort = customAssignmentServerPortString
? atoi(customAssignmentServerPortString) : ASSIGNMENT_SERVER_PORT;
? atoi(customAssignmentServerPortString) : DEFAULT_DOMAIN_SERVER_PORT;
::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* 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})
# 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_executable(${TARGET} ${TARGET_SRCS})

View file

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

View file

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

View file

@ -20,6 +20,11 @@ $(document).ready(function(){
// add the script
+ script + '\r\n'
+ '--' + 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
$.ajax({
@ -27,6 +32,7 @@ $(document).ready(function(){
data: body,
type: "POST",
url: "/assignment",
headers: headers,
success: function (data, status) {
console.log(data);
console.log(status);

View file

@ -27,6 +27,7 @@
#include <stdlib.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QMap>
#include <QtCore/QMutex>
#include <civetweb.h>
@ -58,7 +59,7 @@ unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* no
}
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) {
// return a 200
@ -76,9 +77,20 @@ static int mongooseRequestHandler(struct mg_connection *conn) {
const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment";
static void mongooseUploadHandler(struct mg_connection *conn, const char *path) {
// create an assignment for this saved script, for now make it local only
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);
newPath += "/";
// 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();
::assignmentQueue.push_back(scriptAssignment);
::assignmentQueueMutex.unlock();
}
int main(int argc, const char* argv[]) {
@ -103,7 +114,11 @@ int main(int argc, const char* argv[]) {
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);
@ -121,20 +136,8 @@ int main(int argc, const char* argv[]) {
in_addr_t serverLocalAddress = getLocalAddress();
nodeList->startSilentNodeRemovalThread();
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
// setup the create assignments for those
@ -145,10 +148,20 @@ int main(int argc, const char* argv[]) {
Assignment avatarMixerAssignment(Assignment::CreateCommand,
Assignment::AvatarMixerType,
Assignment::LocalLocation);
Assignment voxelServerAssignment(Assignment::CreateCommand,
Assignment::VoxelServerType,
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
sockaddr_in localSocket = {};
@ -157,13 +170,13 @@ int main(int argc, const char* argv[]) {
localSocket.sin_addr.s_addr = serverLocalAddress;
// setup the mongoose web server
struct mg_context *ctx;
struct mg_context* ctx;
struct mg_callbacks callbacks = {};
QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
// list of options. Last element must be NULL.
const char *options[] = {"listening_ports", "8080",
const char* options[] = {"listening_ports", "8080",
"document_root", documentRoot.toStdString().c_str(), NULL};
callbacks.begin_request = mongooseRequestHandler;
@ -204,10 +217,9 @@ int main(int argc, const char* argv[]) {
}
}
const int MIN_VOXEL_SERVER_CHECKS = 10;
if (checkForVoxelServerAttempt > MIN_VOXEL_SERVER_CHECKS &&
voxelServerCount == 0 &&
if (checkForVoxelServerAttempt > MIN_VOXEL_SERVER_CHECKS && voxelServerCount == 0 &&
std::find(::assignmentQueue.begin(), ::assignmentQueue.end(), &voxelServerAssignment) == ::assignmentQueue.end()) {
qDebug("Missing a Voxel Server and assignment not in queue. Adding.\n");
qDebug("Missing a voxel server and assignment not in queue. Adding.\n");
::assignmentQueue.push_front(&voxelServerAssignment);
}
checkForVoxelServerAttempt++;
@ -237,77 +249,107 @@ int main(int argc, const char* argv[]) {
nodePublicAddress.sin_addr.s_addr = 0;
}
Node* newNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress,
(sockaddr*) &nodeLocalAddress,
nodeType,
nodeList->getLastNodeID());
bool matchedUUID = true;
// if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it
if (newNode) {
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
nodeList->increaseNodeID();
if ((nodeType == NODE_TYPE_AVATAR_MIXER || nodeType == NODE_TYPE_AUDIO_MIXER) &&
!nodeList->soloNodeOfType(nodeType)) {
// if this is an audio-mixer or an avatar-mixer and we don't have one yet
// we need to check the GUID of the assignment in the queue
// (if it exists) to make sure there is a match
// reset matchedUUID to false so there is no match by default
matchedUUID = false;
// pull the UUID passed with the check in
QUuid checkInUUID = QUuid::fromRfc4122(QByteArray((const char*) packetData + numBytesSenderHeader +
sizeof(NODE_TYPE),
NUM_BYTES_RFC4122_UUID));
// lock the assignment queue
::assignmentQueueMutex.lock();
std::deque<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;
startPointer = currentBufferPos;
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + sizeof(NODE_TYPE)
+ numBytesSocket + sizeof(unsigned char);
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
// this is not the node themselves
// and this is an node of a type in the passed node types of interest
// or the node did not pass us any specific types they are interested in
if (memchr(SOLO_NODE_TYPES, node->getType(), sizeof(SOLO_NODE_TYPES)) == NULL) {
// this is an node of which there can be multiple, just add them to the packet
// if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it
if (newNode) {
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
nodeList->increaseNodeID();
}
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
currentBufferPos = broadcastPacket + numHeaderBytes;
startPointer = currentBufferPos;
int numBytesUUID = (nodeType == NODE_TYPE_AUDIO_MIXER || nodeType == NODE_TYPE_AVATAR_MIXER)
? NUM_BYTES_RFC4122_UUID
: 0;
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + numBytesUUID +
sizeof(NODE_TYPE) + numBytesSocket + sizeof(unsigned char);
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
// don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
}
} 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();
soloNode != newestSoloNodes.end();
soloNode++) {
// this is the newest alive solo node, add them to the packet
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, soloNode->second);
}
// update last receive to now
uint64_t timeNow = usecTimestampNow();
newNode->setLastHeardMicrostamp(timeNow);
// add the node ID to the end of the pointer
currentBufferPos += packNodeId(currentBufferPos, newNode->getNodeID());
// send the constructed list back to this node
nodeList->getNodeSocket()->send((sockaddr*)&replyDestinationSocket,
broadcastPacket,
(currentBufferPos - startPointer) + numHeaderBytes);
}
// update last receive to now
uint64_t timeNow = usecTimestampNow();
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) {
@ -320,65 +362,65 @@ int main(int argc, const char* argv[]) {
std::deque<Assignment*>::iterator assignment = ::assignmentQueue.begin();
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
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT);
int numAssignmentBytes = (*assignment)->packToBuffer(broadcastPacket + numHeaderBytes);
nodeList->getNodeSocket()->send((sockaddr*) &nodePublicAddress,
broadcastPacket,
numHeaderBytes + numAssignmentBytes);
// remove the assignment from the queue
::assignmentQueue.erase(assignment);
if ((*assignment)->getType() == Assignment::AgentType) {
// if this is a script assignment we need to delete it to avoid a memory leak
delete *assignment;
if (requestAssignment.getType() == Assignment::AllTypes ||
(*assignment)->getType() == requestAssignment.getType()) {
// attach our local socket to the assignment
(*assignment)->setAttachedLocalSocket((sockaddr*) &localSocket);
// give this assignment out, either the type matches or the requestor said they will take any
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT);
int numAssignmentBytes = (*assignment)->packToBuffer(broadcastPacket + numHeaderBytes);
nodeList->getNodeSocket()->send((sockaddr*) &nodePublicAddress,
broadcastPacket,
numHeaderBytes + numAssignmentBytes);
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();
}
}
// 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()) {
} 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)->getLocation() != Assignment::LocalLocation) {
// attach our local socket to the assignment so the assignment-server can optionally hand it out
(*assignment)->setAttachedLocalSocket((sockaddr*) &localSocket);
nodeList->sendAssignment(*(*assignment));
// remove the assignment from the queue
::assignmentQueue.erase(assignment);
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++;
}
// unpack it
Assignment* createAssignment = new Assignment(packetData, receivedBytes);
qDebug() << "Received a create assignment -" << *createAssignment << "\n";
// add the assignment at the back of the queue
::assignmentQueueMutex.lock();
::assignmentQueue.push_back(createAssignment);
::assignmentQueueMutex.unlock();
}
::assignmentQueueMutex.unlock();
}
if (Logging::shouldSendStats()) {

View file

@ -183,18 +183,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
// --domain or --local options
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
// Voxel File.
_voxelsFilename = getCmdOption(argc, constArgv, "-i");

View file

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

View file

@ -688,6 +688,48 @@ void Menu::aboutApp() {
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() {
Application* applicationInstance = Application::getInstance();
QDialog dialog(applicationInstance->getGLWidget());
@ -698,11 +740,8 @@ void Menu::editPreferences() {
QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1);
const int QLINE_MINIMUM_WIDTH = 400;
QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname()));
domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Domain server:", domainServerHostname);
QLineEdit* domainServerLineEdit = lineEditForDomainHostname();
form->addRow("Domain server:", domainServerLineEdit);
QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString());
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
@ -743,30 +782,7 @@ void Menu::editPreferences() {
return;
}
QByteArray newHostname;
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());
}
updateDSHostname(domainServerLineEdit->text());
QUrl avatarVoxelURL(avatarURL->text());
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
@ -798,12 +814,10 @@ void Menu::goToDomain() {
QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1);
const int QLINE_MINIMUM_WIDTH = 400;
QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname()));
domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Domain server:", domainServerHostname);
QLineEdit* domainServerLineEdit = lineEditForDomainHostname();
form->addRow("Domain server:", domainServerLineEdit);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
@ -816,30 +830,7 @@ void Menu::goToDomain() {
return;
}
QByteArray newHostname;
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());
}
updateDSHostname(domainServerLineEdit->text());
}
void Menu::goToLocation() {

View file

@ -16,10 +16,4 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link 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})
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})

View file

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

View file

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

View file

@ -15,8 +15,11 @@
#include "NodeList.h"
const int NUM_BYTES_RFC4122_UUID = 16;
/// Holds information used for request, creation, and deployment of assignments
class Assignment {
class Assignment : public QObject {
Q_OBJECT
public:
enum Type {
@ -56,12 +59,20 @@ public:
Assignment::Location getLocation() const { return _location; }
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; }
void setAttachedPublicSocket(const sockaddr* attachedPublicSocket);
const sockaddr* getAttachedLocalSocket() { return _attachedLocalSocket; }
void setAttachedLocalSocket(const sockaddr* attachedLocalSocket);
/// Packs the assignment to the passed buffer
/// \param buffer the buffer in which to pack the assignment
/// \return number of bytes packed into buffer
@ -70,6 +81,9 @@ public:
/// Sets _time to the current time given by gettimeofday
void setCreateTimeToNow() { gettimeofday(&_time, NULL); }
/// blocking run of the assignment
virtual void run();
private:
QUuid _uuid; /// the 16 byte UUID for this assignment
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* _attachedLocalSocket; /// pointer to a local socket that relates to assignment, depends on direction
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);

View file

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

View file

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

View file

@ -26,7 +26,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
return 1;
case PACKET_TYPE_VOXEL_STATS:
return 2;
return 2;
default:
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 TEST_COMMAND "a message"
const unsigned short ASSIGNMENT_SERVER_PORT = 7007;
#endif

View file

@ -275,7 +275,7 @@ int UDPSocket::send(sockaddr* destAddress, const void* data, size_t byteLength)
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
destSockaddr.sin_addr.s_addr = inet_addr(destAddress);

View file

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

View file

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

View file

@ -16,10 +16,11 @@ extern EnvironmentData environmentData[3];
#include "VoxelSendThread.h"
#include "VoxelServer.h"
#include "VoxelServerState.h"
#include "VoxelServerConsts.h"
VoxelSendThread::VoxelSendThread(uint16_t nodeID) :
_nodeID(nodeID) {
VoxelSendThread::VoxelSendThread(uint16_t nodeID, VoxelServer* myServer) :
_nodeID(nodeID),
_myServer(myServer) {
}
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
if (nodeData) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
if (::debugVoxelSending) {
if (_myServer->wantsDebugVoxelSending()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
@ -47,7 +48,7 @@ bool VoxelSendThread::process() {
if (usecToSleep > 0) {
usleep(usecToSleep);
} else {
if (::debugVoxelSending) {
if (_myServer->wantsDebugVoxelSending()) {
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
void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) {
pthread_mutex_lock(&::treeLock);
_myServer->lockTree();
int truePacketsSent = 0;
int trueBytesSent = 0;
@ -113,7 +114,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
if (wantColor != nodeData->getCurrentPacketIsColor()) {
if (nodeData->isPacketWaiting()) {
if (::debugVoxelSending) {
if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
}
@ -121,7 +122,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
} else {
if (::debugVoxelSending) {
if (_myServer->wantsDebugVoxelSending()) {
printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n",
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",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
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;
if (::debugVoxelSending) {
if (_myServer->wantsDebugVoxelSending()) {
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
debug::valueOf(nodeData->getViewSent())
@ -148,7 +149,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
// the current view frustum for things to send.
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
uint64_t now = usecTimestampNow();
if (::debugVoxelSending) {
if (_myServer->wantsDebugVoxelSending()) {
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
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 (viewFrustumChanged) {
if (::dumpVoxelsOnMove) {
if (_myServer->wantDumpVoxelsOnMove()) {
nodeData->nodeBag.deleteAll();
}
nodeData->map.erase();
@ -180,7 +181,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
nodeData->stats.sceneCompleted();
if (::displayVoxelStats) {
if (_myServer->wantDisplayVoxelStats()) {
nodeData->stats.printDebugDetails();
}
@ -191,10 +192,10 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
if (isFullScene) {
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.
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...
@ -203,8 +204,8 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
int packetsSentThisInterval = 0;
uint64_t start = usecTimestampNow();
bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
bool shouldSendEnvironments = _myServer->wantSendEnvironments() && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
while (packetsSentThisInterval < _myServer->getPacketsPerClientPerInterval() - (shouldSendEnvironments ? 1 : 0)) {
// Check to see if we're taking too long, and if so bail early...
uint64_t now = usecTimestampNow();
long elapsedUsec = (now - start);
@ -212,7 +213,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec);
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",
usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket,
nodeData->nodeBag.count());
@ -234,10 +235,10 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
wantOcclusionCulling, coverageMap, boundaryLevelAdjust,
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, ::jurisdiction);
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
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->stats.encodeStopped();
@ -254,17 +255,17 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
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
if (shouldSendEnvironments) {
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
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++) {
envPacketLength += environmentData[i].getBroadcastData(_tempOutputBuffer + envPacketLength);
envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_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",
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",
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
}
@ -293,7 +294,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no
if (nodeData->nodeBag.isEmpty()) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
if (::debugVoxelSending) {
if (_myServer->wantsDebugVoxelSending()) {
nodeData->map.printStats();
}
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...
pthread_mutex_unlock(&::treeLock);
_myServer->unlockTree();
}

View file

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

View file

@ -11,10 +11,13 @@
#include <cstring>
#include <cstdio>
#include <QDebug>
#include <QString>
#include <QStringList>
#include <OctalCode.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include <EnvironmentData.h>
#include <VoxelTree.h>
#include "VoxelNodeData.h"
#include <SharedUtil.h>
@ -23,11 +26,6 @@
#include <PerfStat.h>
#include <JurisdictionSender.h>
#include "NodeWatcher.h"
#include "VoxelPersistThread.h"
#include "VoxelSendThread.h"
#include "VoxelServerPacketProcessor.h"
#ifdef _WIN32
#include "Syssocket.h"
#include "Systime.h"
@ -38,34 +36,10 @@
#endif
#include "VoxelServer.h"
#include "VoxelServerState.h"
#include "VoxelServerConsts.h"
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
char voxelPersistFilename[MAX_FILENAME_LENGTH];
int PACKETS_PER_CLIENT_PER_INTERVAL = 10;
VoxelTree serverTree(true); // this IS a reaveraging tree
bool wantVoxelPersist = true;
bool wantLocalDomain = false;
bool debugVoxelSending = false;
bool shouldShowAnimationDebug = false;
bool displayVoxelStats = false;
bool debugVoxelReceiving = false;
bool sendEnvironments = true;
bool sendMinimalEnvironment = false;
bool dumpVoxelsOnMove = false;
EnvironmentData environmentData[3];
int receivedPacketCount = 0;
JurisdictionMap* jurisdiction = NULL;
JurisdictionSender* jurisdictionSender = NULL;
VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL;
VoxelPersistThread* voxelPersistThread = NULL;
pthread_mutex_t treeLock;
NodeWatcher nodeWatcher; // used to cleanup AGENT data when agents are killed
int VoxelServer::_argc = 0;
const char** VoxelServer::_argv = NULL;
bool VoxelServer::_dontKillOnMissingDomain = false;
void attachVoxelNodeDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
@ -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) {
_argc = argc;
_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);
// Handle Local Domain testing with the --local command line
const char* local = "--local";
::wantLocalDomain = strcmp(domain, local) == 0;
if (::wantLocalDomain) {
printf("Local Domain MODE!\n");
_wantLocalDomain = strcmp(domain, local) == 0;
if (_wantLocalDomain) {
qDebug("Local Domain MODE!\n");
NodeList::getInstance()->setDomainIPToLocalhost();
} else {
if (domain) {
@ -99,54 +188,60 @@ void VoxelServer::setupDomainAndPort(const char* domain, int port) {
//int main(int argc, const char * argv[]) {
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);
const char* JURISDICTION_FILE = "--jurisdictionFile";
const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE);
if (jurisdictionFile) {
printf("jurisdictionFile=%s\n", jurisdictionFile);
qDebug("jurisdictionFile=%s\n", jurisdictionFile);
printf("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
jurisdiction = new JurisdictionMap(jurisdictionFile);
printf("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
qDebug("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
_jurisdiction = new JurisdictionMap(jurisdictionFile);
qDebug("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
} else {
const char* JURISDICTION_ROOT = "--jurisdictionRoot";
const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT);
if (jurisdictionRoot) {
printf("jurisdictionRoot=%s\n", jurisdictionRoot);
qDebug("jurisdictionRoot=%s\n", jurisdictionRoot);
}
const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes";
const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES);
if (jurisdictionEndNodes) {
printf("jurisdictionEndNodes=%s\n", jurisdictionEndNodes);
qDebug("jurisdictionEndNodes=%s\n", 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
const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove";
::dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE);
printf("dumpVoxelsOnMove=%s\n", debug::valueOf(::dumpVoxelsOnMove));
_dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE);
qDebug("dumpVoxelsOnMove=%s\n", debug::valueOf(_dumpVoxelsOnMove));
// should we send environments? Default is yes, but this command line suppresses sending
const char* DONT_SEND_ENVIRONMENTS = "--dontSendEnvironments";
bool dontSendEnvironments = getCmdOption(_argc, _argv, DONT_SEND_ENVIRONMENTS);
if (dontSendEnvironments) {
printf("Sending environments suppressed...\n");
::sendEnvironments = false;
qDebug("Sending environments suppressed...\n");
_sendEnvironments = false;
} else {
// should we send environments? Default is yes, but this command line suppresses sending
const char* MINIMAL_ENVIRONMENT = "--MinimalEnvironment";
::sendMinimalEnvironment = getCmdOption(_argc, _argv, MINIMAL_ENVIRONMENT);
printf("Using Minimal Environment=%s\n", debug::valueOf(::sendMinimalEnvironment));
_sendMinimalEnvironment = getCmdOption(_argc, _argv, MINIMAL_ENVIRONMENT);
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->setOwnerType(NODE_TYPE_VOXEL_SERVER);
@ -154,72 +249,72 @@ void VoxelServer::run() {
setvbuf(stdout, NULL, _IOLBF, 0);
// tell our NodeList about our desire to get notifications
nodeList->addHook(&nodeWatcher);
nodeList->addHook(&_nodeWatcher);
nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode;
nodeList->startSilentNodeRemovalThread();
srand((unsigned)time(0));
const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats";
::displayVoxelStats = getCmdOption(_argc, _argv, DISPLAY_VOXEL_STATS);
printf("displayVoxelStats=%s\n", debug::valueOf(::displayVoxelStats));
_displayVoxelStats = getCmdOption(_argc, _argv, DISPLAY_VOXEL_STATS);
qDebug("displayVoxelStats=%s\n", debug::valueOf(_displayVoxelStats));
const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending";
::debugVoxelSending = getCmdOption(_argc, _argv, DEBUG_VOXEL_SENDING);
printf("debugVoxelSending=%s\n", debug::valueOf(::debugVoxelSending));
_debugVoxelSending = getCmdOption(_argc, _argv, DEBUG_VOXEL_SENDING);
qDebug("debugVoxelSending=%s\n", debug::valueOf(_debugVoxelSending));
const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving";
::debugVoxelReceiving = getCmdOption(_argc, _argv, DEBUG_VOXEL_RECEIVING);
printf("debugVoxelReceiving=%s\n", debug::valueOf(::debugVoxelReceiving));
_debugVoxelReceiving = getCmdOption(_argc, _argv, DEBUG_VOXEL_RECEIVING);
qDebug("debugVoxelReceiving=%s\n", debug::valueOf(_debugVoxelReceiving));
const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug";
::shouldShowAnimationDebug = getCmdOption(_argc, _argv, WANT_ANIMATION_DEBUG);
printf("shouldShowAnimationDebug=%s\n", debug::valueOf(::shouldShowAnimationDebug));
_shouldShowAnimationDebug = getCmdOption(_argc, _argv, WANT_ANIMATION_DEBUG);
qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug));
// By default we will voxel persist, if you want to disable this, then pass in this parameter
const char* NO_VOXEL_PERSIST = "--NoVoxelPersist";
if ( getCmdOption(_argc, _argv, NO_VOXEL_PERSIST)) {
::wantVoxelPersist = false;
if (getCmdOption(_argc, _argv, NO_VOXEL_PERSIST)) {
_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...
bool persistantFileRead = false;
if (::wantVoxelPersist) {
if (_wantVoxelPersist) {
// Check to see if the user passed in a command line option for setting packet send rate
const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename";
const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME);
if (voxelsPersistFilenameParameter) {
strcpy(voxelPersistFilename, voxelsPersistFilenameParameter);
strcpy(_voxelPersistFilename, voxelsPersistFilenameParameter);
} else {
//strcpy(voxelPersistFilename, ::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
strcpy(voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE);
//strcpy(voxelPersistFilename, _wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : 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) {
PerformanceWarning warn(::shouldShowAnimationDebug,
"persistVoxelsWhenDirty() - reaverageVoxelColors()", ::shouldShowAnimationDebug);
PerformanceWarning warn(_shouldShowAnimationDebug,
"persistVoxelsWhenDirty() - reaverageVoxelColors()", _shouldShowAnimationDebug);
// after done inserting all these voxels, then reaverage colors
serverTree.reaverageVoxelColors(serverTree.rootNode);
printf("Voxels reAveraged\n");
_serverTree.reaverageVoxelColors(_serverTree.rootNode);
qDebug("Voxels reAveraged\n");
}
::serverTree.clearDirtyBit(); // the tree is clean since we just loaded it
printf("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
unsigned long nodeCount = ::serverTree.rootNode->getSubTreeNodeCount();
unsigned long internalNodeCount = ::serverTree.rootNode->getSubTreeInternalNodeCount();
unsigned long leafNodeCount = ::serverTree.rootNode->getSubTreeLeafNodeCount();
printf("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount);
_serverTree.clearDirtyBit(); // the tree is clean since we just loaded it
qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
unsigned long nodeCount = _serverTree.rootNode->getSubTreeNodeCount();
unsigned long internalNodeCount = _serverTree.rootNode->getSubTreeInternalNodeCount();
unsigned long leafNodeCount = _serverTree.rootNode->getSubTreeLeafNodeCount();
qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount);
// now set up VoxelPersistThread
::voxelPersistThread = new VoxelPersistThread(&::serverTree, ::voxelPersistFilename);
if (::voxelPersistThread) {
::voxelPersistThread->initialize(true);
_voxelPersistThread = new VoxelPersistThread(&_serverTree, _voxelPersistFilename);
if (_voxelPersistThread) {
_voxelPersistThread->initialize(true);
}
}
@ -228,52 +323,52 @@ void VoxelServer::run() {
const char* INPUT_FILE = "-i";
const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE);
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
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND);
if (packetsPerSecond) {
PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/INTERVALS_PER_SECOND;
if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) {
PACKETS_PER_CLIENT_PER_INTERVAL = 1;
_packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND;
if (_packetsPerClientPerInterval < 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
environmentData[1].setID(1);
environmentData[1].setGravity(1.0f);
environmentData[1].setAtmosphereCenter(glm::vec3(0.5, 0.5, (0.25 - 0.06125)) * (float)TREE_SCALE);
environmentData[1].setAtmosphereInnerRadius(0.030625f * TREE_SCALE);
environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f);
environmentData[2].setID(2);
environmentData[2].setGravity(1.0f);
environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE);
environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE);
environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f);
environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue
_environmentData[1].setID(1);
_environmentData[1].setGravity(1.0f);
_environmentData[1].setAtmosphereCenter(glm::vec3(0.5, 0.5, (0.25 - 0.06125)) * (float)TREE_SCALE);
_environmentData[1].setAtmosphereInnerRadius(0.030625f * TREE_SCALE);
_environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f);
_environmentData[2].setID(2);
_environmentData[2].setGravity(1.0f);
_environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE);
_environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE);
_environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f);
_environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue
sockaddr senderAddress;
unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE];
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t packetLength;
timeval lastDomainServerCheckIn = {};
// set up our jurisdiction broadcaster...
::jurisdictionSender = new JurisdictionSender(::jurisdiction);
if (::jurisdictionSender) {
::jurisdictionSender->initialize(true);
_jurisdictionSender = new JurisdictionSender(_jurisdiction);
if (_jurisdictionSender) {
_jurisdictionSender->initialize(true);
}
// set up our VoxelServerPacketProcessor
::voxelServerPacketProcessor = new VoxelServerPacketProcessor();
if (::voxelServerPacketProcessor) {
::voxelServerPacketProcessor->initialize(true);
_voxelServerPacketProcessor = new VoxelServerPacketProcessor(this);
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->initialize(true);
}
// loop to send to nodes requesting data
while (true) {
@ -304,46 +399,50 @@ void VoxelServer::run() {
nodeID);
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) {
// If the packet is a ping, let processNodeData handle it.
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
} else if (packetData[0] == PACKET_TYPE_DOMAIN) {
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
} else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
if (::jurisdictionSender) {
::jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
if (_jurisdictionSender) {
_jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
}
} else if (::voxelServerPacketProcessor) {
::voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
} else if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
} 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) {
::jurisdictionSender->terminate();
delete ::jurisdictionSender;
if (_jurisdictionSender) {
_jurisdictionSender->terminate();
delete _jurisdictionSender;
}
if (::voxelServerPacketProcessor) {
::voxelServerPacketProcessor->terminate();
delete ::voxelServerPacketProcessor;
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->terminate();
delete _voxelServerPacketProcessor;
}
if (::voxelPersistThread) {
::voxelPersistThread->terminate();
delete ::voxelPersistThread;
if (_voxelPersistThread) {
_voxelPersistThread->terminate();
delete _voxelPersistThread;
}
// 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
// voxel-server
//
@ -9,28 +10,84 @@
#ifndef __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.
class VoxelServer {
class VoxelServer : public Assignment {
public:
VoxelServer(Assignment::Command command,
Assignment::Location location = Assignment::GlobalLocation);
VoxelServer(const unsigned char* dataBuffer, int numBytes);
~VoxelServer();
/// runs the voxel server assignment
static void run();
void run();
/// 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
/// and port it is handling. When called by assignment-client, this is not needed because assignment-client
/// handles ports and domains automatically.
/// \param const char* domain domain name, IP address, or local to specify the domain the voxel server is serving
/// \param int port port the voxel server will listen on
static void setupDomainAndPort(const char* domain, int port);
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:
static int _argc;
static const char** _argv;
static bool _dontKillOnMissingDomain;
int _argc;
const char** _argv;
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__

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

View file

@ -12,11 +12,20 @@
#define __voxel_server__VoxelServerPacketProcessor__
#include <ReceivedPacketProcessor.h>
class VoxelServer;
/// 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()
class VoxelServerPacketProcessor : public ReceivedPacketProcessor {
public:
VoxelServerPacketProcessor(VoxelServer* myServer);
protected:
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
private:
VoxelServer* _myServer;
int _receivedPacketCount;
};
#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);
}
VoxelServer ourVoxelServer(Assignment::CreateCommand);
if (wantLocalDomain) {
VoxelServer::setupDomainAndPort(local, listenPort);
ourVoxelServer.setupStandAlone(local, listenPort);
} else {
if (domainIP) {
VoxelServer::setupDomainAndPort(domainIP, listenPort);
ourVoxelServer.setupStandAlone(domainIP, listenPort);
}
}
VoxelServer::setArguments(argc, const_cast<char**>(argv));
VoxelServer::run();
ourVoxelServer.setArguments(argc, const_cast<char**>(argv));
ourVoxelServer.run();
}