mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 18:44:00 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into blendface
This commit is contained in:
commit
1bd8ea947a
48 changed files with 1031 additions and 752 deletions
|
@ -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)
|
||||
|
|
36
README.md
36
README.md
|
@ -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.
|
|
@ -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?
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
36
assignment-client/src/AssignmentFactory.cpp
Normal file
36
assignment-client/src/AssignmentFactory.cpp
Normal 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);
|
||||
}
|
||||
}
|
19
assignment-client/src/AssignmentFactory.h
Normal file
19
assignment-client/src/AssignmentFactory.h
Normal 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__) */
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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__) */
|
|
@ -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) &&
|
|
@ -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__) */
|
|
@ -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);
|
||||
|
||||
|
|
15
assignment-client/src/voxels/VoxelScriptingInterface.cpp
Normal file
15
assignment-client/src/voxels/VoxelScriptingInterface.cpp
Normal 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);
|
||||
}
|
36
assignment-client/src/voxels/VoxelScriptingInterface.h
Normal file
36
assignment-client/src/voxels/VoxelScriptingInterface.h
Normal 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__) */
|
|
@ -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})
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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})
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -26,7 +26,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
return 1;
|
||||
|
||||
case PACKET_TYPE_VOXEL_STATS:
|
||||
return 2;
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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__
|
||||
|
|
31
libraries/voxel-server-library/src/VoxelServerConsts.h
Normal file
31
libraries/voxel-server-library/src/VoxelServerConsts.h
Normal 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__
|
|
@ -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);
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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__
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue