mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 20:44:02 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into particle_details
This commit is contained in:
commit
a9d16862c8
8 changed files with 354 additions and 324 deletions
|
@ -12,6 +12,7 @@
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
@ -19,11 +20,224 @@
|
||||||
|
|
||||||
#include "DomainServer.h"
|
#include "DomainServer.h"
|
||||||
|
|
||||||
|
const int RESTART_HOLD_TIME_MSECS = 5 * 1000;
|
||||||
|
|
||||||
DomainServer* DomainServer::domainServerInstance = NULL;
|
DomainServer* DomainServer::domainServerInstance = NULL;
|
||||||
|
|
||||||
void DomainServer::signalHandler(int signal) {
|
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
domainServerInstance->cleanup();
|
QCoreApplication(argc, argv),
|
||||||
exit(1);
|
_assignmentQueueMutex(),
|
||||||
|
_assignmentQueue(),
|
||||||
|
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
|
||||||
|
_staticAssignmentFileData(NULL),
|
||||||
|
_voxelServerConfig(NULL),
|
||||||
|
_hasCompletedRestartHold(false)
|
||||||
|
{
|
||||||
|
DomainServer::setDomainServerInstance(this);
|
||||||
|
|
||||||
|
const char CUSTOM_PORT_OPTION[] = "-p";
|
||||||
|
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
|
||||||
|
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
|
|
||||||
|
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort);
|
||||||
|
|
||||||
|
const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig";
|
||||||
|
_voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION);
|
||||||
|
|
||||||
|
// setup the mongoose web server
|
||||||
|
struct mg_callbacks callbacks = {};
|
||||||
|
|
||||||
|
QString documentRootString = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
|
||||||
|
|
||||||
|
char documentRoot[documentRootString.size() + 1];
|
||||||
|
strcpy(documentRoot, documentRootString.toLocal8Bit().constData());
|
||||||
|
|
||||||
|
// list of options. Last element must be NULL.
|
||||||
|
const char* options[] = {"listening_ports", "8080",
|
||||||
|
"document_root", documentRoot, NULL};
|
||||||
|
|
||||||
|
callbacks.begin_request = civetwebRequestHandler;
|
||||||
|
callbacks.upload = civetwebUploadHandler;
|
||||||
|
|
||||||
|
// Start the web server.
|
||||||
|
mg_start(&callbacks, NULL, options);
|
||||||
|
|
||||||
|
nodeList->addHook(this);
|
||||||
|
|
||||||
|
if (!_staticAssignmentFile.exists() || _voxelServerConfig) {
|
||||||
|
|
||||||
|
if (_voxelServerConfig) {
|
||||||
|
// we have a new VS config, clear the existing file to start fresh
|
||||||
|
_staticAssignmentFile.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
prepopulateStaticAssignmentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
_staticAssignmentFile.open(QIODevice::ReadWrite);
|
||||||
|
|
||||||
|
_staticAssignmentFileData = _staticAssignmentFile.map(0, _staticAssignmentFile.size());
|
||||||
|
|
||||||
|
_staticAssignments = (Assignment*) _staticAssignmentFileData;
|
||||||
|
|
||||||
|
QTimer* silentNodeTimer = new QTimer(this);
|
||||||
|
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||||
|
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||||
|
|
||||||
|
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams()));
|
||||||
|
|
||||||
|
// fire a single shot timer to add static assignments back into the queue after a restart
|
||||||
|
QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::exit(int retCode) {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::readAvailableDatagrams() {
|
||||||
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
|
HifiSockAddr senderSockAddr, nodePublicAddress, nodeLocalAddress;
|
||||||
|
|
||||||
|
static unsigned char packetData[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
static unsigned char broadcastPacket[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
static unsigned char* currentBufferPos;
|
||||||
|
static unsigned char* startPointer;
|
||||||
|
|
||||||
|
int receivedBytes = 0;
|
||||||
|
|
||||||
|
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||||
|
if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||||
|
senderSockAddr.getAddressPointer(),
|
||||||
|
senderSockAddr.getPortPointer()))
|
||||||
|
&& packetVersionMatch((unsigned char*) packetData)) {
|
||||||
|
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) {
|
||||||
|
// this is an RFD or domain list request packet, and there is a version match
|
||||||
|
|
||||||
|
int numBytesSenderHeader = numBytesForPacketHeader((unsigned char*) packetData);
|
||||||
|
|
||||||
|
NODE_TYPE nodeType = *(packetData + numBytesSenderHeader);
|
||||||
|
|
||||||
|
int packetIndex = numBytesSenderHeader + sizeof(NODE_TYPE);
|
||||||
|
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID));
|
||||||
|
packetIndex += NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
|
int numBytesPrivateSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodePublicAddress);
|
||||||
|
packetIndex += numBytesPrivateSocket;
|
||||||
|
|
||||||
|
if (nodePublicAddress.getAddress().isNull()) {
|
||||||
|
// this node wants to use us its STUN server
|
||||||
|
// so set the node public address to whatever we perceive the public address to be
|
||||||
|
|
||||||
|
// if the sender is on our box then leave its public address to 0 so that
|
||||||
|
// other users attempt to reach it on the same address they have for the domain-server
|
||||||
|
if (senderSockAddr.getAddress().isLoopback()) {
|
||||||
|
nodePublicAddress.setAddress(QHostAddress());
|
||||||
|
} else {
|
||||||
|
nodePublicAddress.setAddress(senderSockAddr.getAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress);
|
||||||
|
packetIndex += numBytesPublicSocket;
|
||||||
|
|
||||||
|
const char STATICALLY_ASSIGNED_NODES[3] = {
|
||||||
|
NODE_TYPE_AUDIO_MIXER,
|
||||||
|
NODE_TYPE_AVATAR_MIXER,
|
||||||
|
NODE_TYPE_VOXEL_SERVER
|
||||||
|
};
|
||||||
|
|
||||||
|
Assignment* matchingStaticAssignment = NULL;
|
||||||
|
|
||||||
|
if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL
|
||||||
|
|| ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|
||||||
|
|| checkInWithUUIDMatchesExistingNode(nodePublicAddress,
|
||||||
|
nodeLocalAddress,
|
||||||
|
nodeUUID)))
|
||||||
|
{
|
||||||
|
Node* checkInNode = nodeList->addOrUpdateNode(nodeUUID,
|
||||||
|
nodeType,
|
||||||
|
nodePublicAddress,
|
||||||
|
nodeLocalAddress);
|
||||||
|
|
||||||
|
if (matchingStaticAssignment) {
|
||||||
|
// this was a newly added node with a matching static assignment
|
||||||
|
|
||||||
|
if (_hasCompletedRestartHold) {
|
||||||
|
// remove the matching assignment from the assignment queue so we don't take the next check in
|
||||||
|
removeAssignmentFromQueue(matchingStaticAssignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the linked data for this node to a copy of the matching assignment
|
||||||
|
// so we can re-queue it should the node die
|
||||||
|
Assignment* nodeCopyOfMatchingAssignment = new Assignment(*matchingStaticAssignment);
|
||||||
|
|
||||||
|
checkInNode->setLinkedData(nodeCopyOfMatchingAssignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
|
||||||
|
|
||||||
|
currentBufferPos = broadcastPacket + numHeaderBytes;
|
||||||
|
startPointer = currentBufferPos;
|
||||||
|
|
||||||
|
unsigned char* nodeTypesOfInterest = packetData + packetIndex + 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->getUUID() != nodeUUID &&
|
||||||
|
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
|
||||||
|
|
||||||
|
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||||
|
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
|
||||||
|
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update last receive to now
|
||||||
|
uint64_t timeNow = usecTimestampNow();
|
||||||
|
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||||
|
|
||||||
|
// send the constructed list back to this node
|
||||||
|
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket,
|
||||||
|
(currentBufferPos - startPointer) + numHeaderBytes,
|
||||||
|
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||||
|
}
|
||||||
|
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
|
||||||
|
|
||||||
|
qDebug("Received a request for assignment.\n");
|
||||||
|
|
||||||
|
if (_assignmentQueue.size() > 0) {
|
||||||
|
// construct the requested assignment from the packet data
|
||||||
|
Assignment requestAssignment(packetData, receivedBytes);
|
||||||
|
|
||||||
|
Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
||||||
|
|
||||||
|
if (assignmentToDeploy) {
|
||||||
|
|
||||||
|
// 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 = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes);
|
||||||
|
|
||||||
|
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, numHeaderBytes + numAssignmentBytes,
|
||||||
|
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||||
|
|
||||||
|
if (assignmentToDeploy->getNumberOfInstances() == 0) {
|
||||||
|
// there are no more instances of this script to send out, delete it
|
||||||
|
delete assignmentToDeploy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::setDomainServerInstance(DomainServer* domainServer) {
|
void DomainServer::setDomainServerInstance(DomainServer* domainServer) {
|
||||||
|
@ -292,55 +506,6 @@ unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosi
|
||||||
return currentPosition;
|
return currentPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
|
||||||
_assignmentQueueMutex(),
|
|
||||||
_assignmentQueue(),
|
|
||||||
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
|
|
||||||
_staticAssignmentFileData(NULL),
|
|
||||||
_voxelServerConfig(NULL),
|
|
||||||
_hasCompletedRestartHold(false)
|
|
||||||
{
|
|
||||||
DomainServer::setDomainServerInstance(this);
|
|
||||||
|
|
||||||
const char CUSTOM_PORT_OPTION[] = "-p";
|
|
||||||
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
|
|
||||||
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
|
||||||
|
|
||||||
NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort);
|
|
||||||
|
|
||||||
struct sigaction sigIntHandler;
|
|
||||||
|
|
||||||
sigIntHandler.sa_handler = DomainServer::signalHandler;
|
|
||||||
sigemptyset(&sigIntHandler.sa_mask);
|
|
||||||
sigIntHandler.sa_flags = 0;
|
|
||||||
|
|
||||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
|
||||||
|
|
||||||
const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig";
|
|
||||||
_voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION);
|
|
||||||
|
|
||||||
const char PARTICLE_CONFIG_OPTION[] = "--particleServerConfig";
|
|
||||||
_particleServerConfig = getCmdOption(argc, (const char**) argv, PARTICLE_CONFIG_OPTION);
|
|
||||||
|
|
||||||
// setup the mongoose web server
|
|
||||||
struct mg_callbacks callbacks = {};
|
|
||||||
|
|
||||||
QString documentRootString = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
|
|
||||||
|
|
||||||
char documentRoot[documentRootString.size() + 1];
|
|
||||||
strcpy(documentRoot, documentRootString.toLocal8Bit().constData());
|
|
||||||
|
|
||||||
// list of options. Last element must be NULL.
|
|
||||||
const char* options[] = {"listening_ports", "8080",
|
|
||||||
"document_root", documentRoot, NULL};
|
|
||||||
|
|
||||||
callbacks.begin_request = civetwebRequestHandler;
|
|
||||||
callbacks.upload = civetwebUploadHandler;
|
|
||||||
|
|
||||||
// Start the web server.
|
|
||||||
mg_start(&callbacks, NULL, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DomainServer::prepopulateStaticAssignmentFile() {
|
void DomainServer::prepopulateStaticAssignmentFile() {
|
||||||
int numFreshStaticAssignments = 0;
|
int numFreshStaticAssignments = 0;
|
||||||
|
|
||||||
|
@ -566,14 +731,12 @@ bool DomainServer::checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePu
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime) {
|
void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
|
||||||
|
_hasCompletedRestartHold = true;
|
||||||
|
|
||||||
// if the domain-server has just restarted,
|
// if the domain-server has just restarted,
|
||||||
// check if there are static assignments in the file that we need to
|
// check if there are static assignments in the file that we need to
|
||||||
// throw into the assignment queue
|
// throw into the assignment queue
|
||||||
const uint64_t RESTART_HOLD_TIME_USECS = 5 * 1000 * 1000;
|
|
||||||
|
|
||||||
if (!_hasCompletedRestartHold && usecTimestampNow() - usecTimestamp(startTime) > RESTART_HOLD_TIME_USECS) {
|
|
||||||
_hasCompletedRestartHold = true;
|
|
||||||
|
|
||||||
// pull anything in the static assignment file that isn't spoken for and add to the assignment queue
|
// pull anything in the static assignment file that isn't spoken for and add to the assignment queue
|
||||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
||||||
|
@ -608,192 +771,11 @@ void DomainServer::possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval*
|
||||||
_assignmentQueueMutex.unlock();
|
_assignmentQueueMutex.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::cleanup() {
|
void DomainServer::cleanup() {
|
||||||
|
qDebug() << "cleanup called!\n";
|
||||||
|
|
||||||
_staticAssignmentFile.unmap(_staticAssignmentFileData);
|
_staticAssignmentFile.unmap(_staticAssignmentFileData);
|
||||||
_staticAssignmentFile.close();
|
_staticAssignmentFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
int DomainServer::run() {
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
|
||||||
|
|
||||||
nodeList->addHook(this);
|
|
||||||
|
|
||||||
ssize_t receivedBytes = 0;
|
|
||||||
char nodeType = '\0';
|
|
||||||
|
|
||||||
unsigned char broadcastPacket[MAX_PACKET_SIZE];
|
|
||||||
unsigned char packetData[MAX_PACKET_SIZE];
|
|
||||||
|
|
||||||
unsigned char* currentBufferPos;
|
|
||||||
unsigned char* startPointer;
|
|
||||||
|
|
||||||
QHostAddress senderAddress;
|
|
||||||
quint16 senderPort;
|
|
||||||
HifiSockAddr nodePublicAddress, nodeLocalAddress;
|
|
||||||
|
|
||||||
nodeList->startSilentNodeRemovalThread();
|
|
||||||
|
|
||||||
if (!_staticAssignmentFile.exists() || _voxelServerConfig) {
|
|
||||||
|
|
||||||
if (_voxelServerConfig) {
|
|
||||||
// we have a new VS config, clear the existing file to start fresh
|
|
||||||
_staticAssignmentFile.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
prepopulateStaticAssignmentFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
_staticAssignmentFile.open(QIODevice::ReadWrite);
|
|
||||||
|
|
||||||
_staticAssignmentFileData = _staticAssignmentFile.map(0, _staticAssignmentFile.size());
|
|
||||||
|
|
||||||
_staticAssignments = (Assignment*) _staticAssignmentFileData;
|
|
||||||
|
|
||||||
timeval startTime;
|
|
||||||
gettimeofday(&startTime, NULL);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
while (nodeList->getNodeSocket().hasPendingDatagrams()
|
|
||||||
&& nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, &senderAddress, &senderPort) &&
|
|
||||||
packetVersionMatch(packetData)) {
|
|
||||||
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) {
|
|
||||||
// this is an RFD or domain list request packet, and there is a version match
|
|
||||||
|
|
||||||
int numBytesSenderHeader = numBytesForPacketHeader(packetData);
|
|
||||||
|
|
||||||
nodeType = *(packetData + numBytesSenderHeader);
|
|
||||||
|
|
||||||
int packetIndex = numBytesSenderHeader + sizeof(NODE_TYPE);
|
|
||||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID));
|
|
||||||
packetIndex += NUM_BYTES_RFC4122_UUID;
|
|
||||||
|
|
||||||
int numBytesPrivateSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodePublicAddress);
|
|
||||||
packetIndex += numBytesPrivateSocket;
|
|
||||||
|
|
||||||
if (nodePublicAddress.getAddress().isNull()) {
|
|
||||||
// this node wants to use us its STUN server
|
|
||||||
// so set the node public address to whatever we perceive the public address to be
|
|
||||||
|
|
||||||
// if the sender is on our box then leave its public address to 0 so that
|
|
||||||
// other users attempt to reach it on the same address they have for the domain-server
|
|
||||||
if (senderAddress.isLoopback()) {
|
|
||||||
nodePublicAddress.setAddress(QHostAddress());
|
|
||||||
} else {
|
|
||||||
nodePublicAddress.setAddress(senderAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress);
|
|
||||||
packetIndex += numBytesPublicSocket;
|
|
||||||
|
|
||||||
const char STATICALLY_ASSIGNED_NODES[3] = {
|
|
||||||
NODE_TYPE_AUDIO_MIXER,
|
|
||||||
NODE_TYPE_AVATAR_MIXER,
|
|
||||||
NODE_TYPE_VOXEL_SERVER
|
|
||||||
};
|
|
||||||
|
|
||||||
Assignment* matchingStaticAssignment = NULL;
|
|
||||||
|
|
||||||
if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL
|
|
||||||
|| ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|
|
||||||
|| checkInWithUUIDMatchesExistingNode(nodePublicAddress,
|
|
||||||
nodeLocalAddress,
|
|
||||||
nodeUUID)))
|
|
||||||
{
|
|
||||||
Node* checkInNode = nodeList->addOrUpdateNode(nodeUUID,
|
|
||||||
nodeType,
|
|
||||||
nodePublicAddress,
|
|
||||||
nodeLocalAddress);
|
|
||||||
|
|
||||||
if (matchingStaticAssignment) {
|
|
||||||
// this was a newly added node with a matching static assignment
|
|
||||||
|
|
||||||
if (_hasCompletedRestartHold) {
|
|
||||||
// remove the matching assignment from the assignment queue so we don't take the next check in
|
|
||||||
removeAssignmentFromQueue(matchingStaticAssignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the linked data for this node to a copy of the matching assignment
|
|
||||||
// so we can re-queue it should the node die
|
|
||||||
Assignment* nodeCopyOfMatchingAssignment = new Assignment(*matchingStaticAssignment);
|
|
||||||
|
|
||||||
checkInNode->setLinkedData(nodeCopyOfMatchingAssignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
|
|
||||||
|
|
||||||
currentBufferPos = broadcastPacket + numHeaderBytes;
|
|
||||||
startPointer = currentBufferPos;
|
|
||||||
|
|
||||||
unsigned char* nodeTypesOfInterest = packetData + packetIndex + 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->getUUID() != nodeUUID &&
|
|
||||||
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
|
|
||||||
|
|
||||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
|
||||||
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
|
|
||||||
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update last receive to now
|
|
||||||
uint64_t timeNow = usecTimestampNow();
|
|
||||||
checkInNode->setLastHeardMicrostamp(timeNow);
|
|
||||||
|
|
||||||
// send the constructed list back to this node
|
|
||||||
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket,
|
|
||||||
(currentBufferPos - startPointer) + numHeaderBytes,
|
|
||||||
senderAddress, senderPort);
|
|
||||||
}
|
|
||||||
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
|
|
||||||
|
|
||||||
qDebug("Received a request for assignment.\n");
|
|
||||||
|
|
||||||
if (!_hasCompletedRestartHold) {
|
|
||||||
possiblyAddStaticAssignmentsBackToQueueAfterRestart(&startTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_assignmentQueue.size() > 0) {
|
|
||||||
// construct the requested assignment from the packet data
|
|
||||||
Assignment requestAssignment(packetData, receivedBytes);
|
|
||||||
|
|
||||||
Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
|
||||||
|
|
||||||
if (assignmentToDeploy) {
|
|
||||||
|
|
||||||
// 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 = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes);
|
|
||||||
|
|
||||||
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, numHeaderBytes + numAssignmentBytes,
|
|
||||||
senderAddress, senderPort);
|
|
||||||
|
|
||||||
if (assignmentToDeploy->getNumberOfInstances() == 0) {
|
|
||||||
// there are no more instances of this script to send out, delete it
|
|
||||||
delete assignmentToDeploy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_hasCompletedRestartHold) {
|
|
||||||
possiblyAddStaticAssignmentsBackToQueueAfterRestart(&startTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->cleanup();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -22,13 +22,13 @@
|
||||||
|
|
||||||
const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000;
|
const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000;
|
||||||
|
|
||||||
class DomainServer : public NodeListHook {
|
class DomainServer : public QCoreApplication, public NodeListHook {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DomainServer(int argc, char* argv[]);
|
DomainServer(int argc, char* argv[]);
|
||||||
|
|
||||||
int run();
|
void exit(int retCode = 0);
|
||||||
|
|
||||||
static void signalHandler(int signal);
|
|
||||||
static void setDomainServerInstance(DomainServer* domainServer);
|
static void setDomainServerInstance(DomainServer* domainServer);
|
||||||
|
|
||||||
/// Called by NodeList to inform us that a node has been added.
|
/// Called by NodeList to inform us that a node has been added.
|
||||||
|
@ -48,7 +48,6 @@ private:
|
||||||
bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
|
bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
|
||||||
const HifiSockAddr& nodeLocalSocket,
|
const HifiSockAddr& nodeLocalSocket,
|
||||||
const QUuid& checkInUUI);
|
const QUuid& checkInUUI);
|
||||||
void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime);
|
|
||||||
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
@ -67,6 +66,9 @@ private:
|
||||||
const char* _particleServerConfig;
|
const char* _particleServerConfig;
|
||||||
|
|
||||||
bool _hasCompletedRestartHold;
|
bool _hasCompletedRestartHold;
|
||||||
|
private slots:
|
||||||
|
void readAvailableDatagrams();
|
||||||
|
void addStaticAssignmentsBackToQueueAfterRestart();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__DomainServer__) */
|
#endif /* defined(__hifi__DomainServer__) */
|
||||||
|
|
|
@ -24,9 +24,8 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
qInstallMessageHandler(Logging::verboseMessageHandler);
|
qInstallMessageHandler(Logging::verboseMessageHandler);
|
||||||
|
|
||||||
QCoreApplication application(argc, argv);
|
|
||||||
DomainServer domainServer(argc, argv);
|
DomainServer domainServer(argc, argv);
|
||||||
|
|
||||||
return domainServer.run();
|
return domainServer.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -360,7 +360,10 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
|
||||||
_collisionSoundNoise(0.0f),
|
_collisionSoundNoise(0.0f),
|
||||||
_collisionSoundDuration(0.0f),
|
_collisionSoundDuration(0.0f),
|
||||||
_proceduralEffectSample(0),
|
_proceduralEffectSample(0),
|
||||||
_heartbeatMagnitude(0.0f),
|
_drumSoundVolume(0),
|
||||||
|
_drumSoundFrequency(0),
|
||||||
|
_drumSoundDuration(0),
|
||||||
|
_drumSoundSample(0),
|
||||||
_muted(false),
|
_muted(false),
|
||||||
_localEcho(false)
|
_localEcho(false)
|
||||||
{
|
{
|
||||||
|
@ -650,6 +653,7 @@ void Audio::addProceduralSounds(int16_t* inputBuffer,
|
||||||
inputBuffer[i] += (int16_t)(sinf((float) (_proceduralEffectSample + i) / SOUND_PITCH ) * volume * (1.f + randFloat() * 0.25f) * speed);
|
inputBuffer[i] += (int16_t)(sinf((float) (_proceduralEffectSample + i) / SOUND_PITCH ) * volume * (1.f + randFloat() * 0.25f) * speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add a collision sound when voxels are run into
|
||||||
const float COLLISION_SOUND_CUTOFF_LEVEL = 0.01f;
|
const float COLLISION_SOUND_CUTOFF_LEVEL = 0.01f;
|
||||||
const float COLLISION_SOUND_MAX_VOLUME = 1000.f;
|
const float COLLISION_SOUND_MAX_VOLUME = 1000.f;
|
||||||
const float UP_MAJOR_FIFTH = powf(1.5f, 4.0f);
|
const float UP_MAJOR_FIFTH = powf(1.5f, 4.0f);
|
||||||
|
@ -673,6 +677,30 @@ void Audio::addProceduralSounds(int16_t* inputBuffer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_proceduralEffectSample += numSamples;
|
_proceduralEffectSample += numSamples;
|
||||||
|
|
||||||
|
// Add a drum sound
|
||||||
|
const float MAX_VOLUME = 32000.f;
|
||||||
|
const float MAX_DURATION = 2.f;
|
||||||
|
const float MIN_AUDIBLE_VOLUME = 0.001f;
|
||||||
|
const float NOISE_MAGNITUDE = 0.02f;
|
||||||
|
float frequency = (_drumSoundFrequency / SAMPLE_RATE) * PI_TIMES_TWO;
|
||||||
|
if (_drumSoundVolume > 0.f) {
|
||||||
|
for (int i = 0; i < numSamples; i++) {
|
||||||
|
t = (float) _drumSoundSample + (float) i;
|
||||||
|
sample = sinf(t * frequency);
|
||||||
|
sample += ((randFloat() - 0.5f) * NOISE_MAGNITUDE);
|
||||||
|
sample *= _drumSoundVolume * MAX_VOLUME;
|
||||||
|
inputBuffer[i] += (int) sample;
|
||||||
|
outputLeft[i] += (int) sample;
|
||||||
|
outputRight[i] += (int) sample;
|
||||||
|
_drumSoundVolume *= (1.f - _drumSoundDecay);
|
||||||
|
}
|
||||||
|
_drumSoundSample += numSamples;
|
||||||
|
_drumSoundDuration = glm::clamp(_drumSoundDuration - (AUDIO_CALLBACK_MSECS / 1000.f), 0.f, MAX_DURATION);
|
||||||
|
if (_drumSoundDuration == 0.f || (_drumSoundVolume < MIN_AUDIBLE_VOLUME)) {
|
||||||
|
_drumSoundVolume = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -685,6 +713,18 @@ void Audio::startCollisionSound(float magnitude, float frequency, float noise, f
|
||||||
_collisionSoundDuration = duration;
|
_collisionSoundDuration = duration;
|
||||||
_collisionFlashesScreen = flashScreen;
|
_collisionFlashesScreen = flashScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Starts a collision sound. magnitude is 0-1, with 1 the loudest possible sound.
|
||||||
|
//
|
||||||
|
void Audio::startDrumSound(float volume, float frequency, float duration, float decay) {
|
||||||
|
_drumSoundVolume = volume;
|
||||||
|
_drumSoundFrequency = frequency;
|
||||||
|
_drumSoundDuration = duration;
|
||||||
|
_drumSoundDecay = decay;
|
||||||
|
_drumSoundSample = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------
|
// -----------------------------------------------------------
|
||||||
// Accoustic ping (audio system round trip time determination)
|
// Accoustic ping (audio system round trip time determination)
|
||||||
// -----------------------------------------------------------
|
// -----------------------------------------------------------
|
||||||
|
|
|
@ -57,6 +57,8 @@ public:
|
||||||
|
|
||||||
void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen);
|
void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen);
|
||||||
|
|
||||||
|
void startDrumSound(float volume, float frequency, float duration, float decay);
|
||||||
|
|
||||||
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; }
|
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; }
|
||||||
|
|
||||||
bool getCollisionFlashesScreen() { return _collisionFlashesScreen; }
|
bool getCollisionFlashesScreen() { return _collisionFlashesScreen; }
|
||||||
|
@ -101,13 +103,21 @@ private:
|
||||||
float _flangeIntensity;
|
float _flangeIntensity;
|
||||||
float _flangeRate;
|
float _flangeRate;
|
||||||
float _flangeWeight;
|
float _flangeWeight;
|
||||||
|
|
||||||
|
// Collision sound generator
|
||||||
float _collisionSoundMagnitude;
|
float _collisionSoundMagnitude;
|
||||||
float _collisionSoundFrequency;
|
float _collisionSoundFrequency;
|
||||||
float _collisionSoundNoise;
|
float _collisionSoundNoise;
|
||||||
float _collisionSoundDuration;
|
float _collisionSoundDuration;
|
||||||
bool _collisionFlashesScreen;
|
bool _collisionFlashesScreen;
|
||||||
int _proceduralEffectSample;
|
int _proceduralEffectSample;
|
||||||
float _heartbeatMagnitude;
|
|
||||||
|
// Drum sound generator
|
||||||
|
float _drumSoundVolume;
|
||||||
|
float _drumSoundFrequency;
|
||||||
|
float _drumSoundDuration;
|
||||||
|
float _drumSoundDecay;
|
||||||
|
int _drumSoundSample;
|
||||||
|
|
||||||
bool _muted;
|
bool _muted;
|
||||||
bool _localEcho;
|
bool _localEcho;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "renderer/ProgramObject.h"
|
#include "renderer/ProgramObject.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Hand::Hand(Avatar* owningAvatar) :
|
Hand::Hand(Avatar* owningAvatar) :
|
||||||
|
@ -126,23 +127,23 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime) {
|
void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime) {
|
||||||
//
|
|
||||||
// Collision between finger and a voxel plays sound
|
// Collision between finger and a voxel plays sound
|
||||||
//
|
const float LOWEST_FREQUENCY = 100.f;
|
||||||
float volume = glm::length(palm->getVelocity());
|
const float HERTZ_PER_RGB = 3.f;
|
||||||
|
const float DECAY_PER_SAMPLE = 0.0005f;
|
||||||
|
const float DURATION_MAX = 2.0f;
|
||||||
|
const float MIN_VOLUME = 0.1f;
|
||||||
|
float volume = MIN_VOLUME + glm::clamp(glm::length(palm->getVelocity()), 0.f, (1.f - MIN_VOLUME));
|
||||||
float duration = volume;
|
float duration = volume;
|
||||||
_collisionCenter = fingerTipPosition;
|
_collisionCenter = fingerTipPosition;
|
||||||
_collisionAge = deltaTime;
|
_collisionAge = deltaTime;
|
||||||
_collisionDuration = duration;
|
_collisionDuration = duration;
|
||||||
int voxelBrightness = voxel->getColor()[0] + voxel->getColor()[1] + voxel->getColor()[2];
|
int voxelBrightness = voxel->getColor()[0] + voxel->getColor()[1] + voxel->getColor()[2];
|
||||||
float frequency = 100.f + (voxelBrightness * 2.f); // Hz
|
float frequency = LOWEST_FREQUENCY + (voxelBrightness * HERTZ_PER_RGB);
|
||||||
// Play a sound
|
Application::getInstance()->getAudio()->startDrumSound(volume,
|
||||||
Application::getInstance()->getAudio()->startCollisionSound(volume,
|
|
||||||
frequency,
|
frequency,
|
||||||
0.25,
|
DURATION_MAX,
|
||||||
0.995f,
|
DECAY_PER_SAMPLE);
|
||||||
false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hand::calculateGeometry() {
|
void Hand::calculateGeometry() {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
SixenseManager::SixenseManager() {
|
SixenseManager::SixenseManager() {
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
sixenseInit();
|
sixenseInit();
|
||||||
|
sixenseSetFilterEnabled(0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +70,12 @@ void SixenseManager::update(float deltaTime) {
|
||||||
palm->setTrigger(data.trigger);
|
palm->setTrigger(data.trigger);
|
||||||
palm->setJoystick(data.joystick_x, data.joystick_y);
|
palm->setJoystick(data.joystick_x, data.joystick_y);
|
||||||
|
|
||||||
|
// Vibrate if needed
|
||||||
|
if (palm->getIsCollidingWithVoxel()) {
|
||||||
|
//printf("vibrate!\n");
|
||||||
|
//vibrate(data.controller_index, 100, 1);
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
|
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
|
||||||
// Adjust for distance between acquisition 'orb' and the user's torso
|
// Adjust for distance between acquisition 'orb' and the user's torso
|
||||||
// (distance to the right of body center, distance below torso, distance behind torso)
|
// (distance to the right of body center, distance below torso, distance behind torso)
|
||||||
|
|
|
@ -75,6 +75,29 @@ OctreeServer::~OctreeServer() {
|
||||||
}
|
}
|
||||||
delete[] _parsedArgV;
|
delete[] _parsedArgV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_jurisdictionSender) {
|
||||||
|
_jurisdictionSender->terminate();
|
||||||
|
delete _jurisdictionSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_octreeInboundPacketProcessor) {
|
||||||
|
_octreeInboundPacketProcessor->terminate();
|
||||||
|
delete _octreeInboundPacketProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_persistThread) {
|
||||||
|
_persistThread->terminate();
|
||||||
|
delete _persistThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tell our NodeList we're done with notifications
|
||||||
|
NodeList::getInstance()->removeHook(this);
|
||||||
|
|
||||||
|
delete _jurisdiction;
|
||||||
|
_jurisdiction = NULL;
|
||||||
|
|
||||||
|
qDebug() << "OctreeServer::run()... DONE\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeServer::initMongoose(int port) {
|
void OctreeServer::initMongoose(int port) {
|
||||||
|
@ -684,38 +707,4 @@ void OctreeServer::run() {
|
||||||
QTimer* pingNodesTimer = new QTimer(this);
|
QTimer* pingNodesTimer = new QTimer(this);
|
||||||
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||||
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||||
|
|
||||||
// loop to send to nodes requesting data
|
|
||||||
while (!_isFinished) {
|
|
||||||
QCoreApplication::processEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
// call NodeList::clear() so that all of our node specific objects, including our sending threads, are
|
|
||||||
// properly shutdown and cleaned up.
|
|
||||||
NodeList::getInstance()->clear();
|
|
||||||
|
|
||||||
if (_jurisdictionSender) {
|
|
||||||
_jurisdictionSender->terminate();
|
|
||||||
delete _jurisdictionSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_octreeInboundPacketProcessor) {
|
|
||||||
_octreeInboundPacketProcessor->terminate();
|
|
||||||
delete _octreeInboundPacketProcessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_persistThread) {
|
|
||||||
_persistThread->terminate();
|
|
||||||
delete _persistThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tell our NodeList we're done with notifications
|
|
||||||
nodeList->removeHook(this);
|
|
||||||
|
|
||||||
delete _jurisdiction;
|
|
||||||
_jurisdiction = NULL;
|
|
||||||
|
|
||||||
qDebug() << "OctreeServer::run()... DONE\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue