mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 02:53:32 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into particle_server
Conflicts: interface/src/VoxelSystem.cpp interface/src/VoxelSystem.h
This commit is contained in:
commit
2b27c91550
84 changed files with 1545 additions and 2197 deletions
CMakeLists.txt
animation-server/src
assignment-client/src
Agent.cppAgent.hAssignmentClient.cppAssignmentClient.hAssignmentFactory.cppAssignmentFactory.h
audio
avatars
main.cppdomain-server/src
interface/src
Application.cppApplication.hAudio.cppDataServerClient.cppDataServerClient.hEnvironment.cppEnvironment.hPairingHandler.cppVoxelHideShowThread.cppVoxelHideShowThread.hVoxelPacketProcessor.cppVoxelPacketProcessor.hVoxelSystem.cppVoxelSystem.h
avatar
devices
starfield
libraries
audio/src
AudioInjectionManager.cppAudioInjectionManager.hAudioInjector.cppAudioInjector.hInjectedAudioRingBuffer.cppInjectedAudioRingBuffer.h
avatars/src
octree/src
JurisdictionListener.cppJurisdictionListener.hJurisdictionSender.cppJurisdictionSender.hOctreeElementBag.h
shared/src
Assignment.cppAssignment.hHifiSockAddr.cppHifiSockAddr.hLogging.cppLogging.hNetworkPacket.cppNetworkPacket.hNode.cppNode.hNodeData.hNodeList.cppNodeList.hOctalCode.cppPacketHeaders.cppPacketSender.cppPacketSender.hReceivedPacketProcessor.cppReceivedPacketProcessor.hThreadedAssignment.cppThreadedAssignment.hUDPSocket.cppUDPSocket.h
voxel-server-library/src
VoxelSendThread.cppVoxelServer.cppVoxelServer.hVoxelServerPacketProcessor.cppVoxelServerPacketProcessor.h
voxels/src
pairing-server
space-server
|
@ -17,5 +17,4 @@ add_subdirectory(assignment-client)
|
|||
add_subdirectory(domain-server)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(pairing-server)
|
||||
add_subdirectory(space-server)
|
||||
add_subdirectory(voxel-edit)
|
|
@ -841,7 +841,7 @@ int main(int argc, const char * argv[])
|
|||
pthread_t animateVoxelThread;
|
||||
pthread_create(&animateVoxelThread, NULL, animateVoxels, NULL);
|
||||
|
||||
sockaddr nodePublicAddress;
|
||||
HifiSockAddr nodeSockAddr;
|
||||
|
||||
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
|
||||
ssize_t receivedBytes;
|
||||
|
@ -858,15 +858,18 @@ int main(int argc, const char * argv[])
|
|||
}
|
||||
|
||||
// Nodes sending messages to us...
|
||||
if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes) &&
|
||||
if (nodeList->getNodeSocket().hasPendingDatagrams()
|
||||
&& (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||
nodeSockAddr.getAddressPointer(),
|
||||
nodeSockAddr.getPortPointer())) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
|
||||
if (::jurisdictionListener) {
|
||||
::jurisdictionListener->queueReceivedPacket(nodePublicAddress, packetData, receivedBytes);
|
||||
::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
NodeList::getInstance()->processNodeData(&nodePublicAddress, packetData, receivedBytes);
|
||||
NodeList::getInstance()->processNodeData(nodeSockAddr, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <AvatarData.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -14,31 +19,12 @@
|
|||
#include <VoxelConstants.h>
|
||||
|
||||
#include "Agent.h"
|
||||
#include "voxels/VoxelScriptingInterface.h"
|
||||
|
||||
Agent::Agent(const unsigned char* dataBuffer, int numBytes) :
|
||||
Assignment(dataBuffer, numBytes),
|
||||
_shouldStop(false)
|
||||
ThreadedAssignment(dataBuffer, numBytes)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Agent::stop() {
|
||||
_shouldStop = true;
|
||||
}
|
||||
|
||||
static size_t writeScriptDataToString(void *contents, size_t size, size_t nmemb, void *userdata) {
|
||||
size_t realSize = size * nmemb;
|
||||
|
||||
QString* scriptContents = (QString*) userdata;
|
||||
|
||||
// append this chunk to the scriptContents
|
||||
scriptContents->append(QByteArray((char*) contents, realSize));
|
||||
|
||||
// return the amount of data read
|
||||
return realSize;
|
||||
}
|
||||
|
||||
QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", vec3.x);
|
||||
|
@ -53,6 +39,16 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) {
|
|||
vec3.z = object.property("z").toVariant().toFloat();
|
||||
}
|
||||
|
||||
void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||
if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
|
||||
_voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
|
||||
(unsigned char*) dataByteArray.data(),
|
||||
dataByteArray.size());
|
||||
} else {
|
||||
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::run() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_AGENT);
|
||||
|
@ -60,149 +56,93 @@ void Agent::run() {
|
|||
const char AGENT_NODE_TYPES_OF_INTEREST[1] = { NODE_TYPE_VOXEL_SERVER };
|
||||
|
||||
nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST));
|
||||
|
||||
nodeList->getNodeSocket()->setBlocking(false);
|
||||
|
||||
// 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(),
|
||||
uuidStringWithoutCurlyBraces(_uuid));
|
||||
|
||||
// setup curl for script download
|
||||
CURLcode curlResult;
|
||||
|
||||
CURL* curlHandle = curl_easy_init();
|
||||
|
||||
// tell curl which file to grab
|
||||
curl_easy_setopt(curlHandle, CURLOPT_URL, scriptURLString.toStdString().c_str());
|
||||
|
||||
// send the data to the WriteMemoryCallback function
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, writeScriptDataToString);
|
||||
|
||||
QString scriptContents;
|
||||
|
||||
// pass the scriptContents QString to append data to
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *)&scriptContents);
|
||||
|
||||
// send a user agent since some servers will require it
|
||||
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||
|
||||
// make sure CURL fails on a 400 code
|
||||
curl_easy_setopt(curlHandle, CURLOPT_FAILONERROR, true);
|
||||
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply *reply = networkManager->get(QNetworkRequest(QUrl(scriptURLString)));
|
||||
|
||||
qDebug() << "Downloading script at" << scriptURLString << "\n";
|
||||
|
||||
// blocking get for JS file
|
||||
curlResult = curl_easy_perform(curlHandle);
|
||||
|
||||
if (curlResult == CURLE_OK) {
|
||||
// cleanup curl
|
||||
curl_easy_cleanup(curlHandle);
|
||||
curl_global_cleanup();
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
|
||||
loop.exec();
|
||||
|
||||
QString scriptContents(reply->readAll());
|
||||
QScriptEngine engine;
|
||||
|
||||
// register meta-type for glm::vec3 conversions
|
||||
qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue);
|
||||
|
||||
QScriptValue agentValue = engine.newQObject(this);
|
||||
engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
||||
QScriptValue voxelScripterValue = engine.newQObject(&_voxelScriptingInterface);
|
||||
engine.globalObject().setProperty("Voxels", voxelScripterValue);
|
||||
|
||||
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
|
||||
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
|
||||
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
|
||||
|
||||
// let the VoxelPacketSender know how frequently we plan to call it
|
||||
_voxelScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptContents << "\n";
|
||||
QScriptValue result = engine.evaluate(scriptContents);
|
||||
qDebug() << "Evaluated script.\n";
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
|
||||
}
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
int thisFrame = 0;
|
||||
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
QTimer* pingNodesTimer = new QTimer(this);
|
||||
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||
|
||||
while (!_isFinished) {
|
||||
|
||||
QScriptEngine engine;
|
||||
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
||||
// register meta-type for glm::vec3 conversions
|
||||
qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue);
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QScriptValue agentValue = engine.newQObject(this);
|
||||
engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
||||
VoxelScriptingInterface voxelScripter;
|
||||
QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter);
|
||||
engine.globalObject().setProperty("Voxels", voxelScripterValue);
|
||||
|
||||
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
|
||||
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
|
||||
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
|
||||
|
||||
// let the VoxelPacketSender know how frequently we plan to call it
|
||||
voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
|
||||
|
||||
// hook in a constructor for audio injectorss
|
||||
AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
QScriptValue audioInjectorValue = engine.newQObject(&scriptedAudioInjector);
|
||||
engine.globalObject().setProperty("AudioInjector", audioInjectorValue);
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptContents << "\n";
|
||||
QScriptValue result = engine.evaluate(scriptContents);
|
||||
qDebug() << "Evaluated script.\n";
|
||||
if (_voxelScriptingInterface.getVoxelPacketSender()->voxelServersExist()) {
|
||||
// allow the scripter's call back to setup visual data
|
||||
emit willSendVisualDataCallback();
|
||||
|
||||
// release the queue of edit voxel messages.
|
||||
_voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();
|
||||
|
||||
// since we're in non-threaded mode, call process so that the packets are sent
|
||||
_voxelScriptingInterface.getVoxelPacketSender()->process();
|
||||
}
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
|
||||
}
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
|
||||
sockaddr_in senderAddress;
|
||||
unsigned char receivedData[MAX_PACKET_SIZE];
|
||||
ssize_t receivedBytes;
|
||||
|
||||
int thisFrame = 0;
|
||||
|
||||
NodeList::getInstance()->startSilentNodeRemovalThread();
|
||||
|
||||
while (!_shouldStop) {
|
||||
|
||||
// if we're not hearing from the domain-server we should stop running
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
break;
|
||||
}
|
||||
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
||||
if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) {
|
||||
timeval thisSend = {};
|
||||
gettimeofday(&thisSend, NULL);
|
||||
// allow the scripter's call back to setup visual data
|
||||
emit willSendVisualDataCallback();
|
||||
|
||||
// release the queue of edit voxel messages.
|
||||
voxelScripter.getVoxelPacketSender()->releaseQueuedMessages();
|
||||
|
||||
// since we're in non-threaded mode, call process so that the packets are sent
|
||||
voxelScripter.getVoxelPacketSender()->process();
|
||||
}
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
|
||||
}
|
||||
|
||||
while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)
|
||||
&& packetVersionMatch(receivedData)) {
|
||||
if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
|
||||
voxelScripter.getJurisdictionListener()->queueReceivedPacket((sockaddr&) senderAddress,
|
||||
receivedData,
|
||||
receivedBytes);
|
||||
} else {
|
||||
NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeList::getInstance()->stopSilentNodeRemovalThread();
|
||||
|
||||
} else {
|
||||
// error in curl_easy_perform
|
||||
qDebug() << "curl_easy_perform for JS failed:" << curl_easy_strerror(curlResult) << "\n";
|
||||
|
||||
// cleanup curl
|
||||
curl_easy_cleanup(curlHandle);
|
||||
curl_global_cleanup();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,25 +15,24 @@
|
|||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <AudioInjector.h>
|
||||
#include <Assignment.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
class Agent : public Assignment {
|
||||
#include "voxels/VoxelScriptingInterface.h"
|
||||
|
||||
class Agent : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Agent(const unsigned char* dataBuffer, int numBytes);
|
||||
|
||||
void run();
|
||||
public slots:
|
||||
void stop();
|
||||
void run();
|
||||
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
signals:
|
||||
void willSendAudioDataCallback();
|
||||
void willSendVisualDataCallback();
|
||||
private:
|
||||
static QScriptValue AudioInjectorConstructor(QScriptContext *context, QScriptEngine *engine);
|
||||
|
||||
bool volatile _shouldStop;
|
||||
std::vector<AudioInjector*> _audioInjectors;
|
||||
VoxelScriptingInterface _voxelScriptingInterface;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Agent__) */
|
||||
|
|
140
assignment-client/src/AssignmentClient.cpp
Normal file
140
assignment-client/src/AssignmentClient.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
//
|
||||
// AssignmentClient.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 11/25/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <Assignment.h>
|
||||
#include <Logging.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
|
||||
#include "AssignmentClient.h"
|
||||
|
||||
const char ASSIGNMENT_CLIENT_TARGET_NAME[] = "assignment-client";
|
||||
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
|
||||
|
||||
AssignmentClient::AssignmentClient(int &argc, char **argv,
|
||||
Assignment::Type requestAssignmentType,
|
||||
const HifiSockAddr& customAssignmentServerSocket,
|
||||
const char* requestAssignmentPool) :
|
||||
QCoreApplication(argc, argv),
|
||||
_requestAssignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool),
|
||||
_currentAssignment(NULL)
|
||||
{
|
||||
// register meta type is required for queued invoke method on Assignment subclasses
|
||||
qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
||||
// create a NodeList as an unassigned client
|
||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED);
|
||||
|
||||
// set the custom assignment socket if we have it
|
||||
if (!customAssignmentServerSocket.isNull()) {
|
||||
nodeList->setAssignmentServerSocket(customAssignmentServerSocket);
|
||||
}
|
||||
|
||||
// call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
|
||||
qDebug() << "Waiting for assignment -" << _requestAssignment << "\n";
|
||||
|
||||
QTimer* timer = new QTimer(this);
|
||||
connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
|
||||
timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS);
|
||||
|
||||
// connect our readPendingDatagrams method to the readyRead() signal of the socket
|
||||
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
|
||||
}
|
||||
|
||||
void AssignmentClient::sendAssignmentRequest() {
|
||||
if (!_currentAssignment) {
|
||||
NodeList::getInstance()->sendAssignment(_requestAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::readPendingDatagrams() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
static unsigned char packetData[1500];
|
||||
static qint64 receivedBytes = 0;
|
||||
static HifiSockAddr senderSockAddr;
|
||||
|
||||
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||
|
||||
if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||
senderSockAddr.getAddressPointer(),
|
||||
senderSockAddr.getPortPointer()))
|
||||
&& packetVersionMatch(packetData)) {
|
||||
|
||||
if (_currentAssignment) {
|
||||
// have the threaded current assignment handle this datagram
|
||||
QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection,
|
||||
Q_ARG(const QByteArray&, QByteArray((char*) packetData, receivedBytes)),
|
||||
Q_ARG(const HifiSockAddr&, senderSockAddr));
|
||||
} else if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
|
||||
|
||||
if (_currentAssignment) {
|
||||
qDebug() << "Dropping received assignment since we are currently running one.\n";
|
||||
} else {
|
||||
// construct the deployed assignment from the packet data
|
||||
_currentAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes);
|
||||
|
||||
qDebug() << "Received an assignment -" << *_currentAssignment << "\n";
|
||||
|
||||
// switch our nodelist domain IP and port to whoever sent us the assignment
|
||||
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
|
||||
nodeList->setDomainSockAddr(senderSockAddr);
|
||||
nodeList->setOwnerUUID(_currentAssignment->getUUID());
|
||||
|
||||
qDebug("Destination IP for assignment is %s\n",
|
||||
nodeList->getDomainIP().toString().toStdString().c_str());
|
||||
|
||||
// start the deployed assignment
|
||||
QThread* workerThread = new QThread(this);
|
||||
|
||||
connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run()));
|
||||
|
||||
connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted()));
|
||||
connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit()));
|
||||
connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater()));
|
||||
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
|
||||
|
||||
_currentAssignment->moveToThread(workerThread);
|
||||
|
||||
// Starts an event loop, and emits workerThread->started()
|
||||
workerThread->start();
|
||||
} else {
|
||||
qDebug("Received a bad destination socket for assignment.\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// have the NodeList attempt to handle it
|
||||
nodeList->processNodeData(senderSockAddr, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::assignmentCompleted() {
|
||||
// reset the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
||||
qDebug("Assignment finished or never started - waiting for new assignment\n");
|
||||
|
||||
_currentAssignment = NULL;
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
// reset our NodeList by switching back to unassigned and clearing the list
|
||||
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
|
||||
nodeList->reset();
|
||||
}
|
32
assignment-client/src/AssignmentClient.h
Normal file
32
assignment-client/src/AssignmentClient.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// AssignmentClient.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 11/25/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AssignmentClient__
|
||||
#define __hifi__AssignmentClient__
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "ThreadedAssignment.h"
|
||||
|
||||
class AssignmentClient : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssignmentClient(int &argc, char **argv,
|
||||
Assignment::Type requestAssignmentType = Assignment::AllTypes,
|
||||
const HifiSockAddr& customAssignmentServerSocket = HifiSockAddr(),
|
||||
const char* requestAssignmentPool = NULL);
|
||||
private slots:
|
||||
void sendAssignmentRequest();
|
||||
void readPendingDatagrams();
|
||||
void assignmentCompleted();
|
||||
private:
|
||||
Assignment _requestAssignment;
|
||||
ThreadedAssignment* _currentAssignment;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AssignmentClient__) */
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "AssignmentFactory.h"
|
||||
|
||||
Assignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) {
|
||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) {
|
||||
int headerBytes = numBytesForPacketHeader(dataBuffer);
|
||||
|
||||
Assignment::Type assignmentType = Assignment::AllTypes;
|
||||
|
@ -31,6 +31,6 @@ Assignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer,
|
|||
case Assignment::VoxelServerType:
|
||||
return new VoxelServer(dataBuffer, numBytes);
|
||||
default:
|
||||
return new Assignment(dataBuffer, numBytes);
|
||||
return NULL;
|
||||
}
|
||||
}
|
|
@ -9,11 +9,11 @@
|
|||
#ifndef __hifi__AssignmentFactory__
|
||||
#define __hifi__AssignmentFactory__
|
||||
|
||||
#include "Assignment.h"
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
class AssignmentFactory {
|
||||
public:
|
||||
static Assignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes);
|
||||
static ThreadedAssignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AssignmentFactory__) */
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <Logging.h>
|
||||
#include <NodeList.h>
|
||||
#include <Node.h>
|
||||
|
@ -51,7 +54,7 @@
|
|||
const short JITTER_BUFFER_MSECS = 12;
|
||||
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
|
||||
|
||||
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
|
||||
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000 * 1000);
|
||||
|
||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
|
||||
const int MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
|
||||
|
@ -64,7 +67,9 @@ void attachNewBufferToNode(Node *newNode) {
|
|||
}
|
||||
}
|
||||
|
||||
AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
|
||||
AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) :
|
||||
ThreadedAssignment(dataBuffer, numBytes)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
@ -217,78 +222,77 @@ void AudioMixer::prepareMixForListeningNode(Node* node) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||
// pull any new audio data from nodes off of the network stack
|
||||
if (dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO
|
||||
|| dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|
||||
|| dataByteArray[0] == PACKET_TYPE_INJECT_AUDIO) {
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
Node* matchingNode = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (matchingNode) {
|
||||
nodeList->updateNodeWithData(matchingNode, senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
|
||||
if (!matchingNode->getActiveSocket()) {
|
||||
// we don't have an active socket for this node, but they're talking to us
|
||||
// this means they've heard from us and can reply, let's assume public is active
|
||||
matchingNode->activatePublicSocket();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::run() {
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
// change the logging target name while this is running
|
||||
Logging::setTargetName(AUDIO_MIXER_LOGGING_TARGET_NAME);
|
||||
|
||||
NodeList *nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER);
|
||||
|
||||
const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR };
|
||||
nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST));
|
||||
|
||||
ssize_t receivedBytes = 0;
|
||||
|
||||
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
||||
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
|
||||
unsigned char packetData[MAX_PACKET_SIZE] = {};
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
sockaddr* nodeAddress = new sockaddr;
|
||||
|
||||
// make sure our node socket is non-blocking
|
||||
nodeList->getNodeSocket()->setBlocking(false);
|
||||
QTimer* pingNodesTimer = new QTimer(this);
|
||||
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||
|
||||
int nextFrame = 0;
|
||||
timeval startTime;
|
||||
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MIXED_AUDIO);
|
||||
unsigned char clientPacket[BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader];
|
||||
populateTypeAndVersion(clientPacket, PACKET_TYPE_MIXED_AUDIO);
|
||||
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
|
||||
timeval beginSendTime, endSendTime;
|
||||
float sumFrameTimePercentages = 0.0f;
|
||||
int numStatCollections = 0;
|
||||
|
||||
// if we'll be sending stats, call the Logstash::socket() method to make it load the logstash IP outside the loop
|
||||
if (Logging::shouldSendStats()) {
|
||||
Logging::socket();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
while (!_isFinished) {
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
if (_isFinished) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (Logging::shouldSendStats()) {
|
||||
gettimeofday(&beginSendTime, NULL);
|
||||
}
|
||||
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
|
||||
if (Logging::shouldSendStats() && numStatCollections > 0) {
|
||||
// if we should be sending stats to Logstash send the appropriate average now
|
||||
const char MIXER_LOGSTASH_METRIC_NAME[] = "audio-mixer-frame-time-usage";
|
||||
|
||||
float averageFrameTimePercentage = sumFrameTimePercentages / numStatCollections;
|
||||
Logging::stashValue(STAT_TYPE_TIMER, MIXER_LOGSTASH_METRIC_NAME, averageFrameTimePercentage);
|
||||
|
||||
sumFrameTimePercentages = 0.0f;
|
||||
numStatCollections = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// get the NodeList to ping any inactive nodes, for hole punching
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData()) {
|
||||
((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES);
|
||||
|
@ -301,7 +305,9 @@ void AudioMixer::run() {
|
|||
prepareMixForListeningNode(&(*node));
|
||||
|
||||
memcpy(clientPacket + numBytesPacketHeader, _clientSamples, sizeof(_clientSamples));
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), clientPacket, sizeof(clientPacket));
|
||||
nodeList->getNodeSocket().writeDatagram((char*) clientPacket, sizeof(clientPacket),
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,47 +318,6 @@ void AudioMixer::run() {
|
|||
}
|
||||
}
|
||||
|
||||
// pull any new audio data from nodes off of the network stack
|
||||
while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO
|
||||
|| packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|
||||
|| packetData[0] == PACKET_TYPE_INJECT_AUDIO) {
|
||||
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
Node* matchingNode = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (matchingNode) {
|
||||
nodeList->updateNodeWithData(matchingNode, nodeAddress, packetData, receivedBytes);
|
||||
|
||||
if (!matchingNode->getActiveSocket()) {
|
||||
// we don't have an active socket for this node, but they're talking to us
|
||||
// this means they've heard from us and can reply, let's assume public is active
|
||||
matchingNode->activatePublicSocket();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
if (Logging::shouldSendStats()) {
|
||||
// send a packet to our logstash instance
|
||||
|
||||
// calculate the percentage value for time elapsed for this send (of the max allowable time)
|
||||
gettimeofday(&endSendTime, NULL);
|
||||
|
||||
float percentageOfMaxElapsed = ((float) (usecTimestamp(&endSendTime) - usecTimestamp(&beginSendTime))
|
||||
/ BUFFER_SEND_INTERVAL_USECS) * 100.0f;
|
||||
|
||||
sumFrameTimePercentages += percentageOfMaxElapsed;
|
||||
|
||||
numStatCollections++;
|
||||
}
|
||||
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
|
@ -360,5 +325,6 @@ void AudioMixer::run() {
|
|||
} else {
|
||||
qDebug("Took too much time, not sleeping!\n");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -9,19 +9,23 @@
|
|||
#ifndef __hifi__AudioMixer__
|
||||
#define __hifi__AudioMixer__
|
||||
|
||||
#include <Assignment.h>
|
||||
#include <AudioRingBuffer.h>
|
||||
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
class PositionalAudioRingBuffer;
|
||||
class AvatarAudioRingBuffer;
|
||||
|
||||
/// Handles assignments of type AudioMixer - mixing streams of audio and re-distributing to various clients.
|
||||
class AudioMixer : public Assignment {
|
||||
class AudioMixer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioMixer(const unsigned char* dataBuffer, int numBytes);
|
||||
|
||||
/// runs the audio mixer
|
||||
public slots:
|
||||
/// threaded run of assignment
|
||||
void run();
|
||||
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
private:
|
||||
/// adds one buffer to the mix for a listening node
|
||||
void addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd,
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
// The avatar mixer receives head, hand and positional data from all connected
|
||||
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <Logging.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
@ -22,6 +25,14 @@
|
|||
|
||||
const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer";
|
||||
|
||||
const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
|
||||
|
||||
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) :
|
||||
ThreadedAssignment(dataBuffer, numBytes)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
|
||||
QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122();
|
||||
memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size());
|
||||
|
@ -46,7 +57,7 @@ void attachAvatarDataToNode(Node* newNode) {
|
|||
// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to
|
||||
// determine which avatars are included in the packet stream
|
||||
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
|
||||
void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, sockaddr* receiverAddress) {
|
||||
void broadcastAvatarData() {
|
||||
static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE];
|
||||
static unsigned char avatarDataBuffer[MAX_PACKET_SIZE];
|
||||
unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0];
|
||||
|
@ -55,38 +66,91 @@ void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, sockaddr
|
|||
int packetLength = currentBufferPosition - broadcastPacket;
|
||||
int packetsSent = 0;
|
||||
|
||||
// send back a packet with other active node data to this node
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() && node->getUUID() != receiverUUID) {
|
||||
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node);
|
||||
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
|
||||
|
||||
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
|
||||
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
||||
packetLength += avatarDataLength;
|
||||
currentBufferPosition += avatarDataLength;
|
||||
} else {
|
||||
packetsSent++;
|
||||
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
||||
nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
|
||||
|
||||
// reset the packet
|
||||
currentBufferPosition = broadcastPacket + numHeaderBytes;
|
||||
packetLength = currentBufferPosition - broadcastPacket;
|
||||
|
||||
// copy the avatar that didn't fit into the next packet
|
||||
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
||||
packetLength += avatarDataLength;
|
||||
currentBufferPosition += avatarDataLength;
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT && node->getActiveSocket()) {
|
||||
// this is an AGENT we have received head data from
|
||||
// send back a packet with other active node data to this node
|
||||
for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) {
|
||||
if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) {
|
||||
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node);
|
||||
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
|
||||
|
||||
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
|
||||
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
||||
packetLength += avatarDataLength;
|
||||
currentBufferPosition += avatarDataLength;
|
||||
} else {
|
||||
packetsSent++;
|
||||
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
||||
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
|
||||
// reset the packet
|
||||
currentBufferPosition = broadcastPacket + numHeaderBytes;
|
||||
packetLength = currentBufferPosition - broadcastPacket;
|
||||
|
||||
// copy the avatar that didn't fit into the next packet
|
||||
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
||||
packetLength += avatarDataLength;
|
||||
currentBufferPosition += avatarDataLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packetsSent++;
|
||||
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
}
|
||||
}
|
||||
packetsSent++;
|
||||
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
||||
nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
|
||||
|
||||
|
||||
}
|
||||
|
||||
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
|
||||
void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
switch (dataByteArray[0]) {
|
||||
case PACKET_TYPE_HEAD_DATA: {
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// add or update the node in our list
|
||||
Node* avatarNode = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (avatarNode) {
|
||||
// parse positional data from an node
|
||||
nodeList->updateNodeWithData(avatarNode, senderSockAddr,
|
||||
(unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
case PACKET_TYPE_KILL_NODE:
|
||||
case PACKET_TYPE_AVATAR_URLS:
|
||||
case PACKET_TYPE_AVATAR_FACE_VIDEO: {
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
// let everyone else know about the update
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getActiveSocket() && node->getUUID() != nodeUUID) {
|
||||
nodeList->getNodeSocket().writeDatagram(dataByteArray,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
}
|
||||
}
|
||||
// let node kills fall through to default behavior
|
||||
}
|
||||
default:
|
||||
// hand this off to the NodeList
|
||||
nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -101,71 +165,39 @@ void AvatarMixer::run() {
|
|||
|
||||
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
||||
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
|
||||
sockaddr nodeAddress = {};
|
||||
ssize_t receivedBytes = 0;
|
||||
QTimer* pingNodesTimer = new QTimer(this);
|
||||
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||
|
||||
unsigned char packetData[MAX_PACKET_SIZE];
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
QUuid nodeUUID;
|
||||
Node* avatarNode = NULL;
|
||||
int nextFrame = 0;
|
||||
timeval startTime;
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
while (true) {
|
||||
while (!_isFinished) {
|
||||
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
if (_isFinished) {
|
||||
break;
|
||||
}
|
||||
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
broadcastAvatarData();
|
||||
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
switch (packetData[0]) {
|
||||
case PACKET_TYPE_HEAD_DATA:
|
||||
nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// add or update the node in our list
|
||||
avatarNode = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (avatarNode) {
|
||||
// parse positional data from an node
|
||||
nodeList->updateNodeWithData(avatarNode, &nodeAddress, packetData, receivedBytes);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_INJECT_AUDIO:
|
||||
broadcastAvatarData(nodeList, nodeUUID, &nodeAddress);
|
||||
break;
|
||||
case PACKET_TYPE_KILL_NODE:
|
||||
case PACKET_TYPE_AVATAR_URLS:
|
||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
||||
nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
// let everyone else know about the update
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getActiveSocket() && node->getUUID() != nodeUUID) {
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
// let node kills fall through to default behavior
|
||||
|
||||
default:
|
||||
// hand this off to the NodeList
|
||||
nodeList->processNodeData(&nodeAddress, packetData, receivedBytes);
|
||||
break;
|
||||
}
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
qDebug() << "Took too much time, not sleeping!\n";
|
||||
}
|
||||
}
|
||||
|
||||
nodeList->stopSilentNodeRemovalThread();
|
||||
}
|
||||
|
|
|
@ -9,15 +9,18 @@
|
|||
#ifndef __hifi__AvatarMixer__
|
||||
#define __hifi__AvatarMixer__
|
||||
|
||||
#include <Assignment.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
||||
class AvatarMixer : public Assignment {
|
||||
class AvatarMixer : public ThreadedAssignment {
|
||||
public:
|
||||
AvatarMixer(const unsigned char* dataBuffer, int numBytes);
|
||||
|
||||
public slots:
|
||||
/// runs the avatar mixer
|
||||
void run();
|
||||
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AvatarMixer__) */
|
||||
|
|
|
@ -20,16 +20,14 @@
|
|||
|
||||
#include "Agent.h"
|
||||
#include "Assignment.h"
|
||||
#include "AssignmentFactory.h"
|
||||
#include "AssignmentClient.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";
|
||||
|
||||
pid_t* childForks = NULL;
|
||||
sockaddr_in customAssignmentSocket = {};
|
||||
HifiSockAddr customAssignmentSocket;
|
||||
int numForks = 0;
|
||||
Assignment::Type overiddenAssignmentType = Assignment::AllTypes;
|
||||
const char* assignmentPool = NULL;
|
||||
|
@ -37,84 +35,9 @@ const char* assignmentPool = NULL;
|
|||
int argc = 0;
|
||||
char** argv = NULL;
|
||||
|
||||
void childClient() {
|
||||
QCoreApplication application(::argc, ::argv);
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(CHILD_TARGET_NAME);
|
||||
|
||||
// create a NodeList as an unassigned client
|
||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED);
|
||||
|
||||
// set the custom assignment socket if we have it
|
||||
if (customAssignmentSocket.sin_addr.s_addr != 0) {
|
||||
nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket);
|
||||
}
|
||||
|
||||
// change the timeout on the nodelist socket to be as often as we want to re-request
|
||||
nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS);
|
||||
|
||||
timeval lastRequest = {};
|
||||
|
||||
unsigned char packetData[MAX_PACKET_SIZE];
|
||||
ssize_t receivedBytes = 0;
|
||||
|
||||
sockaddr_in senderSocket = {};
|
||||
|
||||
// create a request assignment, accept assignments defined by the overidden type
|
||||
Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType, ::assignmentPool);
|
||||
|
||||
qDebug() << "Waiting for assignment -" << requestAssignment << "\n";
|
||||
|
||||
while (true) {
|
||||
if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) {
|
||||
gettimeofday(&lastRequest, NULL);
|
||||
|
||||
// if we're here we have no assignment, so send a request
|
||||
nodeList->sendAssignment(requestAssignment);
|
||||
}
|
||||
|
||||
if (nodeList->getNodeSocket()->receive((sockaddr*) &senderSocket, packetData, &receivedBytes) &&
|
||||
(packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT)
|
||||
&& packetVersionMatch(packetData)) {
|
||||
|
||||
// construct the deployed assignment from the packet data
|
||||
Assignment* deployedAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes);
|
||||
|
||||
qDebug() << "Received an assignment -" << *deployedAssignment << "\n";
|
||||
|
||||
// switch our nodelist domain IP and port to whoever sent us the assignment
|
||||
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
|
||||
|
||||
nodeList->setDomainIP(QHostAddress((sockaddr*) &senderSocket));
|
||||
nodeList->setDomainPort(ntohs(senderSocket.sin_port));
|
||||
|
||||
nodeList->setOwnerUUID(deployedAssignment->getUUID());
|
||||
|
||||
qDebug("Destination IP for assignment is %s\n", nodeList->getDomainIP().toString().toStdString().c_str());
|
||||
|
||||
// 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->reset();
|
||||
|
||||
// set the NodeList socket back to blocking
|
||||
nodeList->getNodeSocket()->setBlocking(true);
|
||||
|
||||
// reset the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(CHILD_TARGET_NAME);
|
||||
}
|
||||
}
|
||||
int childClient() {
|
||||
AssignmentClient client(::argc, ::argv, ::overiddenAssignmentType, customAssignmentSocket, ::assignmentPool);
|
||||
return client.exec();
|
||||
}
|
||||
|
||||
void sigchldHandler(int sig) {
|
||||
|
@ -203,10 +126,10 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
// set the custom hostname or default if it wasn't passed
|
||||
if (!customAssignmentServerHostname) {
|
||||
customAssignmentServerHostname = LOCAL_ASSIGNMENT_SERVER_HOSTNAME;
|
||||
customAssignmentServerHostname = DEFAULT_ASSIGNMENT_SERVER_HOSTNAME;
|
||||
}
|
||||
|
||||
::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServerHostname, assignmentServerPort);
|
||||
::customAssignmentSocket = HifiSockAddr(customAssignmentServerHostname, assignmentServerPort);
|
||||
}
|
||||
|
||||
const char ASSIGNMENT_TYPE_OVVERIDE_OPTION[] = "-t";
|
||||
|
@ -247,7 +170,7 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
if (processID == 0 || ::numForks == 0) {
|
||||
childClient();
|
||||
return childClient();
|
||||
} else {
|
||||
parentMonitor();
|
||||
}
|
||||
|
|
|
@ -30,14 +30,11 @@ void DomainServer::setDomainServerInstance(DomainServer* domainServer) {
|
|||
domainServerInstance = domainServer;
|
||||
}
|
||||
|
||||
QJsonObject jsonForSocket(sockaddr* socket) {
|
||||
QJsonObject jsonForSocket(const HifiSockAddr& socket) {
|
||||
QJsonObject socketJSON;
|
||||
|
||||
if (socket->sa_family == AF_INET) {
|
||||
sockaddr_in* socketIPv4 = (sockaddr_in*) socket;
|
||||
socketJSON["ip"] = QString(inet_ntoa(socketIPv4->sin_addr));
|
||||
socketJSON["port"] = (int) ntohs(socketIPv4->sin_port);
|
||||
}
|
||||
socketJSON["ip"] = socket.getAddress().toString();
|
||||
socketJSON["port"] = ntohs(socket.getPort());
|
||||
|
||||
return socketJSON;
|
||||
}
|
||||
|
@ -288,8 +285,8 @@ unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosi
|
|||
memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size());
|
||||
currentPosition += rfcUUID.size();
|
||||
|
||||
currentPosition += packSocket(currentPosition, nodeToAdd->getPublicSocket());
|
||||
currentPosition += packSocket(currentPosition, nodeToAdd->getLocalSocket());
|
||||
currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getPublicSocket());
|
||||
currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getLocalSocket());
|
||||
|
||||
// return the new unsigned char * for broadcast packet
|
||||
return currentPosition;
|
||||
|
@ -506,15 +503,15 @@ void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) {
|
|||
_assignmentQueueMutex.unlock();
|
||||
}
|
||||
|
||||
bool DomainServer::checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket,
|
||||
sockaddr* nodeLocalSocket,
|
||||
bool DomainServer::checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
|
||||
const HifiSockAddr& nodeLocalSocket,
|
||||
const QUuid& checkInUUID) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData()
|
||||
&& socketMatch(node->getPublicSocket(), nodePublicSocket)
|
||||
&& socketMatch(node->getLocalSocket(), nodeLocalSocket)
|
||||
&& nodePublicSocket == node->getPublicSocket()
|
||||
&& nodeLocalSocket == node->getLocalSocket()
|
||||
&& node->getUUID() == checkInUUID) {
|
||||
// this is a matching existing node if the public socket, local socket, and UUID match
|
||||
return true;
|
||||
|
@ -588,9 +585,9 @@ int DomainServer::run() {
|
|||
unsigned char* currentBufferPos;
|
||||
unsigned char* startPointer;
|
||||
|
||||
sockaddr_in senderAddress, nodePublicAddress, nodeLocalAddress;
|
||||
nodePublicAddress.sin_family = AF_INET;
|
||||
nodeLocalAddress.sin_family = AF_INET;
|
||||
QHostAddress senderAddress;
|
||||
quint16 senderPort;
|
||||
HifiSockAddr nodePublicAddress, nodeLocalAddress;
|
||||
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
|
||||
|
@ -614,7 +611,8 @@ int DomainServer::run() {
|
|||
gettimeofday(&startTime, NULL);
|
||||
|
||||
while (true) {
|
||||
while (nodeList->getNodeSocket()->receive((sockaddr *)&senderAddress, packetData, &receivedBytes) &&
|
||||
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
|
||||
|
@ -627,23 +625,23 @@ int DomainServer::run() {
|
|||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID));
|
||||
packetIndex += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
int numBytesPrivateSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodePublicAddress);
|
||||
int numBytesPrivateSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodePublicAddress);
|
||||
packetIndex += numBytesPrivateSocket;
|
||||
|
||||
if (nodePublicAddress.sin_addr.s_addr == 0) {
|
||||
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
|
||||
|
||||
nodePublicAddress = senderAddress;
|
||||
|
||||
// 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.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
|
||||
nodePublicAddress.sin_addr.s_addr = 0;
|
||||
if (senderAddress.isLoopback()) {
|
||||
nodePublicAddress.setAddress(QHostAddress());
|
||||
} else {
|
||||
nodePublicAddress.setAddress(senderAddress);
|
||||
}
|
||||
}
|
||||
|
||||
int numBytesPublicSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodeLocalAddress);
|
||||
int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress);
|
||||
packetIndex += numBytesPublicSocket;
|
||||
|
||||
const char STATICALLY_ASSIGNED_NODES[3] = {
|
||||
|
@ -656,14 +654,14 @@ int DomainServer::run() {
|
|||
|
||||
if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL
|
||||
|| ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|
||||
|| checkInWithUUIDMatchesExistingNode((sockaddr*) &nodePublicAddress,
|
||||
(sockaddr*) &nodeLocalAddress,
|
||||
|| checkInWithUUIDMatchesExistingNode(nodePublicAddress,
|
||||
nodeLocalAddress,
|
||||
nodeUUID)))
|
||||
{
|
||||
Node* checkInNode = nodeList->addOrUpdateNode(nodeUUID,
|
||||
nodeType,
|
||||
(sockaddr*) &nodePublicAddress,
|
||||
(sockaddr*) &nodeLocalAddress);
|
||||
nodePublicAddress,
|
||||
nodeLocalAddress);
|
||||
|
||||
if (matchingStaticAssignment) {
|
||||
// this was a newly added node with a matching static assignment
|
||||
|
@ -691,7 +689,7 @@ int DomainServer::run() {
|
|||
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) &&
|
||||
if (node->getUUID() != nodeUUID &&
|
||||
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
|
@ -708,9 +706,9 @@ int DomainServer::run() {
|
|||
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
// send the constructed list back to this node
|
||||
nodeList->getNodeSocket()->send((sockaddr*)&senderAddress,
|
||||
broadcastPacket,
|
||||
(currentBufferPos - startPointer) + numHeaderBytes);
|
||||
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket,
|
||||
(currentBufferPos - startPointer) + numHeaderBytes,
|
||||
senderAddress, senderPort);
|
||||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
|
||||
|
||||
|
@ -732,9 +730,8 @@ int DomainServer::run() {
|
|||
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT);
|
||||
int numAssignmentBytes = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes);
|
||||
|
||||
nodeList->getNodeSocket()->send((sockaddr*) &senderAddress,
|
||||
broadcastPacket,
|
||||
numHeaderBytes + numAssignmentBytes);
|
||||
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
|
||||
|
@ -743,43 +740,6 @@ int DomainServer::run() {
|
|||
}
|
||||
|
||||
}
|
||||
} 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
|
||||
|
||||
// unpack it
|
||||
Assignment* createAssignment = new Assignment(packetData, receivedBytes);
|
||||
|
||||
qDebug() << "Received a create assignment -" << *createAssignment << "\n";
|
||||
|
||||
// make sure we have a matching node with the UUID packed with the assignment
|
||||
// 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->getLinkedData()
|
||||
&& socketMatch((sockaddr*) &senderAddress, node->getPublicSocket())
|
||||
&& ((Assignment*) node->getLinkedData())->getUUID() == createAssignment->getUUID()) {
|
||||
|
||||
// give the create assignment a new UUID
|
||||
createAssignment->resetUUID();
|
||||
|
||||
// add the assignment at the back of the queue
|
||||
_assignmentQueueMutex.lock();
|
||||
_assignmentQueue.push_back(createAssignment);
|
||||
_assignmentQueueMutex.unlock();
|
||||
|
||||
// find the first available spot in the static assignments and put this assignment there
|
||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
||||
if (_staticAssignments[i].getUUID().isNull()) {
|
||||
_staticAssignments[i] = *createAssignment;
|
||||
|
||||
// we've stuck the assignment in, break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we found the matching node that asked for create assignment, break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,9 @@ private:
|
|||
Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType);
|
||||
Assignment* deployableAssignmentForRequest(Assignment& requestAssignment);
|
||||
void removeAssignmentFromQueue(Assignment* removableAssignment);
|
||||
bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const QUuid& checkInUUI);
|
||||
bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
|
||||
const HifiSockAddr& nodeLocalSocket,
|
||||
const QUuid& checkInUUI);
|
||||
void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime);
|
||||
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
|
@ -47,8 +48,6 @@
|
|||
#include <QDesktopServices>
|
||||
|
||||
#include <NodeTypes.h>
|
||||
#include <AudioInjectionManager.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <Logging.h>
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
@ -126,6 +125,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_mouseVoxelScale(1.0f / 1024.0f),
|
||||
_mouseVoxelScaleInitialized(false),
|
||||
_justEditedVoxel(false),
|
||||
_isHighlightVoxel(false),
|
||||
_nudgeStarted(false),
|
||||
_lookingAlongX(false),
|
||||
_lookingAwayFromOrigin(true),
|
||||
|
@ -138,6 +138,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
#endif
|
||||
_stopNetworkReceiveThread(false),
|
||||
_voxelProcessor(),
|
||||
_voxelHideShowThread(&_voxels),
|
||||
_voxelEditSender(this),
|
||||
_packetCount(0),
|
||||
_packetsPerSecond(0),
|
||||
|
@ -170,9 +171,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
// network receive thread and voxel parsing thread are both controlled by the --nonblocking command line
|
||||
_enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking");
|
||||
if (!_enableNetworkThread) {
|
||||
NodeList::getInstance()->getNodeSocket()->setBlocking(false);
|
||||
}
|
||||
|
||||
// setup QSettings
|
||||
#ifdef Q_OS_MAC
|
||||
|
@ -316,6 +314,7 @@ void Application::initializeGL() {
|
|||
// create thread for parsing of voxel data independent of the main network and rendering threads
|
||||
_voxelProcessor.initialize(_enableProcessVoxelsThread);
|
||||
_voxelEditSender.initialize(_enableProcessVoxelsThread);
|
||||
_voxelHideShowThread.initialize(_enableProcessVoxelsThread);
|
||||
if (_enableProcessVoxelsThread) {
|
||||
qDebug("Voxel parsing thread created.\n");
|
||||
}
|
||||
|
@ -1385,6 +1384,7 @@ void Application::terminate() {
|
|||
}
|
||||
|
||||
_voxelProcessor.terminate();
|
||||
_voxelHideShowThread.terminate();
|
||||
_voxelEditSender.terminate();
|
||||
}
|
||||
|
||||
|
@ -1445,8 +1445,9 @@ void Application::checkBandwidthMeterClick() {
|
|||
// ... to be called upon button release
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth) &&
|
||||
glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY))) <= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH &&
|
||||
_bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) {
|
||||
glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))
|
||||
<= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH
|
||||
&& _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) {
|
||||
|
||||
// The bandwidth meter is visible, the click didn't get dragged too far and
|
||||
// we actually hit the bandwidth meter
|
||||
|
@ -1756,8 +1757,6 @@ void Application::init() {
|
|||
_voxelShader.init();
|
||||
_pointShader.init();
|
||||
|
||||
_handControl.setScreenDimensions(_glWidget->width(), _glWidget->height());
|
||||
|
||||
_headMouseX = _mouseX = _glWidget->width() / 2;
|
||||
_headMouseY = _mouseY = _glWidget->height() / 2;
|
||||
QCursor::setPos(_headMouseX, _headMouseY);
|
||||
|
@ -1982,6 +1981,20 @@ void Application::renderFollowIndicator() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::renderHighlightVoxel(VoxelDetail voxel) {
|
||||
glDisable(GL_LIGHTING);
|
||||
glPushMatrix();
|
||||
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||
const float EDGE_EXPAND = 1.02f;
|
||||
glColor3ub(voxel.red + 128, voxel.green + 128, voxel.blue + 128);
|
||||
glTranslatef(voxel.x + voxel.s * 0.5f,
|
||||
voxel.y + voxel.s * 0.5f,
|
||||
voxel.z + voxel.s * 0.5f);
|
||||
glLineWidth(2.0f);
|
||||
glutWireCube(voxel.s * EDGE_EXPAND);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
||||
|
@ -2227,11 +2240,6 @@ void Application::updateHandAndTouch(float deltaTime) {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()");
|
||||
|
||||
// walking triggers the handControl to stop
|
||||
if (_myAvatar.getMode() == AVATAR_MODE_WALKING) {
|
||||
_handControl.stop();
|
||||
}
|
||||
|
||||
// Update from Touch
|
||||
if (_isTouchPressed) {
|
||||
float TOUCH_YAW_SCALE = -0.25f;
|
||||
|
@ -2281,6 +2289,7 @@ void Application::updateThreads(float deltaTime) {
|
|||
// parse voxel packets
|
||||
if (!_enableProcessVoxelsThread) {
|
||||
_voxelProcessor.threadRoutine();
|
||||
_voxelHideShowThread.threadRoutine();
|
||||
_voxelEditSender.threadRoutine();
|
||||
}
|
||||
}
|
||||
|
@ -2690,7 +2699,6 @@ void Application::queryVoxels() {
|
|||
qDebug("perServerPPS: %d perUnknownServer: %d\n", perServerPPS, perUnknownServer);
|
||||
}
|
||||
|
||||
UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
|
||||
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
|
@ -2776,7 +2784,8 @@ void Application::queryVoxels() {
|
|||
|
||||
int packetLength = endOfVoxelQueryPacket - voxelQueryPacket;
|
||||
|
||||
nodeSocket->send(node->getActiveSocket(), voxelQueryPacket, packetLength);
|
||||
nodeList->getNodeSocket().writeDatagram((char*) voxelQueryPacket, packetLength,
|
||||
node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort());
|
||||
|
||||
// Feed number of bytes to corresponding channel of the bandwidth meter
|
||||
_bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(packetLength);
|
||||
|
@ -3012,10 +3021,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// restore default, white specular
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
|
||||
|
||||
// Render the highlighted voxel
|
||||
if (_isHighlightVoxel) {
|
||||
renderHighlightVoxel(_highlightVoxel);
|
||||
}
|
||||
|
||||
// indicate what we'll be adding/removing in mouse mode, if anything
|
||||
if (_mouseVoxel.s != 0 && whichCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
|
@ -3039,7 +3052,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
} else {
|
||||
renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
}
|
||||
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) {
|
||||
// use a contrasting color so that we can see what we're doing
|
||||
glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128);
|
||||
|
@ -3924,62 +3937,6 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::injectVoxelAddedSoundEffect() {
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
|
||||
|
||||
if (voxelInjector) {
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
//voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
|
||||
voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value
|
||||
|
||||
/* for (int i = 0; i
|
||||
< 22050; i++) {
|
||||
if (i % 4 == 0) {
|
||||
voxelInjector->addSample(4000);
|
||||
} else if (i % 4 == 1) {
|
||||
voxelInjector->addSample(0);
|
||||
} else if (i % 4 == 2) {
|
||||
voxelInjector->addSample(-4000);
|
||||
} else {
|
||||
voxelInjector->addSample(0);
|
||||
}
|
||||
*/
|
||||
|
||||
const float BIG_VOXEL_MIN_SIZE = .01f;
|
||||
|
||||
for (int i = 0; i < 11025; i++) {
|
||||
|
||||
/*
|
||||
A440 square wave
|
||||
if (sin(i * 2 * PIE / 50)>=0) {
|
||||
voxelInjector->addSample(4000);
|
||||
} else {
|
||||
voxelInjector->addSample(-4000);
|
||||
}
|
||||
*/
|
||||
|
||||
if (_mouseVoxel.s > BIG_VOXEL_MIN_SIZE) {
|
||||
voxelInjector->addSample(20000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 200))));
|
||||
} else {
|
||||
voxelInjector->addSample(16000 * sin(i / (1.5 * log (_mouseVoxel.s / .0001) * ((i + 11025) / 5512.5)))); //808
|
||||
}
|
||||
}
|
||||
|
||||
//voxelInjector->addSample(32500 * sin(i/(2 * 1 * ((i+5000)/5512.5)))); //80
|
||||
//voxelInjector->addSample(20000 * sin(i/(6 * (_mouseVoxel.s/.001) *((i+5512.5)/5512.5)))); //808
|
||||
//voxelInjector->addSample(20000 * sin(i/(6 * ((i+5512.5)/5512.5)))); //808
|
||||
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50)); //A440 sine wave
|
||||
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50) * sin (i/500)); //A440 sine wave with amplitude modulation
|
||||
|
||||
//FM library
|
||||
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(500*sin((i+1)/200)))); //FM 1 dubstep
|
||||
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(300*sin((i+1)/5.0)))); //FM 2 flange sweep
|
||||
//voxelInjector->addSample(10000 * sin((i * 2 * PIE) /(500*sin((i+1)/500.0)))); //FM 3 resonant pulse
|
||||
|
||||
AudioInjectionManager::threadInjector(voxelInjector);
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::maybeEditVoxelUnderCursor() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)
|
||||
|| Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode)) {
|
||||
|
@ -3992,9 +3949,6 @@ bool Application::maybeEditVoxelUnderCursor() {
|
|||
_mouseVoxel.green,
|
||||
_mouseVoxel.blue,
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel));
|
||||
|
||||
// inject a sound effect
|
||||
injectVoxelAddedSoundEffect();
|
||||
|
||||
// remember the position for drag detection
|
||||
_justEditedVoxel = true;
|
||||
|
@ -4028,21 +3982,6 @@ void Application::deleteVoxelUnderCursor() {
|
|||
// delete it locally to see the effect immediately (and in case no voxel server is present)
|
||||
_voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
|
||||
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
|
||||
|
||||
if (voxelInjector) {
|
||||
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
|
||||
//voxelInjector->setBearing(0); //straight down the z axis
|
||||
voxelInjector->setVolume (255); //255 is max, and also default value
|
||||
|
||||
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse
|
||||
//voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter
|
||||
}
|
||||
|
||||
AudioInjectionManager::threadInjector(voxelInjector);
|
||||
}
|
||||
}
|
||||
// remember the position for drag detection
|
||||
_justEditedVoxel = true;
|
||||
|
@ -4197,10 +4136,10 @@ void Application::nodeKilled(Node* node) {
|
|||
}
|
||||
|
||||
void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength,
|
||||
sockaddr senderAddress, bool wasStatsPacket) {
|
||||
const HifiSockAddr& senderSockAddr, bool wasStatsPacket) {
|
||||
|
||||
// Attempt to identify the sender from it's address.
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (voxelServer) {
|
||||
QUuid nodeUUID = voxelServer->getUUID();
|
||||
|
||||
|
@ -4214,10 +4153,10 @@ void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t m
|
|||
}
|
||||
}
|
||||
|
||||
int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress) {
|
||||
int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr) {
|
||||
|
||||
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
|
||||
// parse the incoming stats datas stick it in a temporary object for now, while we
|
||||
// determine which server it belongs to
|
||||
|
@ -4270,12 +4209,16 @@ void* Application::networkReceive(void* args) {
|
|||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::networkReceive()");
|
||||
|
||||
sockaddr senderAddress;
|
||||
HifiSockAddr senderSockAddr;
|
||||
ssize_t bytesReceived;
|
||||
|
||||
Application* app = Application::getInstance();
|
||||
while (!app->_stopNetworkReceiveThread) {
|
||||
if (NodeList::getInstance()->getNodeSocket()->receive(&senderAddress, app->_incomingPacket, &bytesReceived)) {
|
||||
if (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() &&
|
||||
(bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) app->_incomingPacket,
|
||||
MAX_PACKET_SIZE,
|
||||
senderSockAddr.getAddressPointer(),
|
||||
senderSockAddr.getPortPointer()))) {
|
||||
|
||||
app->_packetCount++;
|
||||
app->_bytesCount += bytesReceived;
|
||||
|
@ -4314,11 +4257,11 @@ void* Application::networkReceive(void* args) {
|
|||
}
|
||||
|
||||
// add this packet to our list of voxel packets and process them on the voxel processing
|
||||
app->_voxelProcessor.queueReceivedPacket(senderAddress, app->_incomingPacket, bytesReceived);
|
||||
app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_BULK_AVATAR_DATA:
|
||||
NodeList::getInstance()->processBulkNodeData(&senderAddress,
|
||||
NodeList::getInstance()->processBulkNodeData(senderSockAddr,
|
||||
app->_incomingPacket,
|
||||
bytesReceived);
|
||||
getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
|
||||
|
@ -4336,7 +4279,7 @@ void* Application::networkReceive(void* args) {
|
|||
DataServerClient::processMessageFromDataServer(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
default:
|
||||
NodeList::getInstance()->processNodeData(&senderAddress, app->_incomingPacket, bytesReceived);
|
||||
NodeList::getInstance()->processNodeData(senderSockAddr, app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,13 +41,13 @@
|
|||
#include "ViewFrustum.h"
|
||||
#include "VoxelFade.h"
|
||||
#include "VoxelEditPacketSender.h"
|
||||
#include "VoxelHideShowThread.h"
|
||||
#include "VoxelPacketProcessor.h"
|
||||
#include "VoxelSystem.h"
|
||||
#include "VoxelImporter.h"
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "avatar/Profile.h"
|
||||
#include "avatar/HandControl.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/SerialInterface.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
|
@ -188,6 +188,10 @@ public:
|
|||
glm::vec2 getViewportDimensions() const{ return glm::vec2(_glWidget->width(),_glWidget->height()); }
|
||||
NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; }
|
||||
void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination);
|
||||
|
||||
/// set a voxel which is to be rendered with a highlight
|
||||
void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; }
|
||||
void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; }
|
||||
|
||||
public slots:
|
||||
void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data);
|
||||
|
@ -231,6 +235,7 @@ private slots:
|
|||
void shrinkMirrorView();
|
||||
void resetSensors();
|
||||
|
||||
|
||||
private:
|
||||
void resetCamerasOnResizeGL(Camera& camera, int width, int height);
|
||||
void updateProjectionMatrix();
|
||||
|
@ -272,9 +277,11 @@ private:
|
|||
Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
|
||||
glm::vec3& eyePosition, QUuid &nodeUUID);
|
||||
bool isLookingAtMyAvatar(Avatar* avatar);
|
||||
|
||||
|
||||
void renderLookatIndicator(glm::vec3 pointOfInterest);
|
||||
void renderFollowIndicator();
|
||||
void renderHighlightVoxel(VoxelDetail voxel);
|
||||
|
||||
void updateAvatar(float deltaTime);
|
||||
void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection);
|
||||
void queryVoxels();
|
||||
|
@ -293,7 +300,6 @@ private:
|
|||
bool maybeEditVoxelUnderCursor();
|
||||
void deleteVoxelUnderCursor();
|
||||
void eyedropperVoxelUnderCursor();
|
||||
void injectVoxelAddedSoundEffect();
|
||||
|
||||
void setMenuShortcutsEnabled(bool enabled);
|
||||
|
||||
|
@ -371,8 +377,6 @@ private:
|
|||
|
||||
int _headMouseX, _headMouseY;
|
||||
|
||||
HandControl _handControl;
|
||||
|
||||
int _mouseX;
|
||||
int _mouseY;
|
||||
int _mouseDragStartedX;
|
||||
|
@ -405,6 +409,9 @@ private:
|
|||
glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit
|
||||
bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel
|
||||
|
||||
VoxelDetail _highlightVoxel;
|
||||
bool _isHighlightVoxel;
|
||||
|
||||
VoxelDetail _nudgeVoxel; // details of the voxel to be nudged
|
||||
bool _nudgeStarted;
|
||||
bool _lookingAlongX;
|
||||
|
@ -440,8 +447,9 @@ private:
|
|||
bool _stopNetworkReceiveThread;
|
||||
|
||||
bool _enableProcessVoxelsThread;
|
||||
VoxelPacketProcessor _voxelProcessor;
|
||||
VoxelEditPacketSender _voxelEditSender;
|
||||
VoxelPacketProcessor _voxelProcessor;
|
||||
VoxelHideShowThread _voxelHideShowThread;
|
||||
VoxelEditPacketSender _voxelEditSender;
|
||||
|
||||
unsigned char _incomingPacket[MAX_PACKET_SIZE];
|
||||
int _packetCount;
|
||||
|
@ -462,9 +470,9 @@ private:
|
|||
|
||||
PieMenu _pieMenu;
|
||||
|
||||
int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress);
|
||||
void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength,
|
||||
sockaddr senderAddress, bool wasStatsPacket);
|
||||
int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderAddress);
|
||||
void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength,
|
||||
const HifiSockAddr& senderSockAddr, bool wasStatsPacket);
|
||||
|
||||
NodeToJurisdictionMap _voxelServerJurisdictions;
|
||||
NodeToVoxelSceneStats _voxelServerSceneStats;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StdDev.h>
|
||||
#include <UDPSocket.h>
|
||||
#include <QSvgRenderer>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -145,9 +144,9 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
|
|||
// copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet
|
||||
memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES_PER_CHANNEL);
|
||||
|
||||
nodeList->getNodeSocket()->send(audioMixer->getActiveSocket(),
|
||||
dataPacket,
|
||||
BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes);
|
||||
nodeList->getNodeSocket().writeDatagram((char*) dataPacket, BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes,
|
||||
audioMixer->getActiveSocket()->getAddress(),
|
||||
audioMixer->getActiveSocket()->getPort());
|
||||
|
||||
interface->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO).updateValue(BUFFER_LENGTH_BYTES_PER_CHANNEL
|
||||
+ leadingBytes);
|
||||
|
@ -289,7 +288,7 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
|
|||
// add output (@speakers) data just written to the scope
|
||||
_scope->addSamples(1, outputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
_scope->addSamples(2, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
||||
|
||||
gettimeofday(&_lastCallbackTime, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
//
|
||||
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <UDPSocket.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -24,7 +24,12 @@ const char MULTI_KEY_VALUE_SEPARATOR = '|';
|
|||
|
||||
const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io";
|
||||
const unsigned short DATA_SERVER_PORT = 3282;
|
||||
const sockaddr_in DATA_SERVER_SOCKET = socketForHostnameAndHostOrderPort(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT);
|
||||
|
||||
|
||||
const HifiSockAddr& DataServerClient::dataServerSockAddr() {
|
||||
static HifiSockAddr dsSockAddr = HifiSockAddr(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT);
|
||||
return dsSockAddr;
|
||||
}
|
||||
|
||||
void DataServerClient::putValueForKey(const QString& key, const char* value) {
|
||||
QString clientString = Application::getInstance()->getProfile()->getUserString();
|
||||
|
@ -57,7 +62,9 @@ void DataServerClient::putValueForKey(const QString& key, const char* value) {
|
|||
// _unmatchedPackets.insert(std::pair<unsigned char*, int>(putPacket, numPacketBytes));
|
||||
|
||||
// send this put request to the data server
|
||||
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, putPacket, numPacketBytes);
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) putPacket, numPacketBytes,
|
||||
dataServerSockAddr().getAddress(),
|
||||
dataServerSockAddr().getPort());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +103,9 @@ void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, co
|
|||
// _unmatchedPackets.insert(std::pair<unsigned char*, int>(getPacket, numPacketBytes));
|
||||
|
||||
// send the get to the data server
|
||||
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, getPacket, numPacketBytes);
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) getPacket, numPacketBytes,
|
||||
dataServerSockAddr().getAddress(),
|
||||
dataServerSockAddr().getPort());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,8 +245,8 @@ void DataServerClient::resendUnmatchedPackets() {
|
|||
mapIterator != _unmatchedPackets.end();
|
||||
++mapIterator) {
|
||||
// send the unmatched packet to the data server
|
||||
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET,
|
||||
mapIterator->first,
|
||||
mapIterator->second);
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) mapIterator->first, mapIterator->second,
|
||||
dataServerSockAddr().getAddress(),
|
||||
dataServerSockAddr().getPort());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
class DataServerClient {
|
||||
public:
|
||||
static const HifiSockAddr& dataServerSockAddr();
|
||||
static void putValueForKey(const QString& key, const char* value);
|
||||
static void getClientValueForKey(const QString& key);
|
||||
|
||||
|
|
|
@ -18,24 +18,13 @@
|
|||
#include "renderer/ProgramObject.h"
|
||||
#include "world.h"
|
||||
|
||||
uint qHash(const sockaddr& address) {
|
||||
const sockaddr_in* inetAddress = reinterpret_cast<const sockaddr_in*>(&address);
|
||||
if (inetAddress->sin_family != AF_INET) {
|
||||
uint qHash(const HifiSockAddr& sockAddr) {
|
||||
if (sockAddr.getAddress().isNull()) {
|
||||
return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash
|
||||
}
|
||||
return inetAddress->sin_port + qHash(QByteArray::fromRawData(
|
||||
reinterpret_cast<const char*>(&inetAddress->sin_addr), sizeof(in_addr)));
|
||||
}
|
||||
|
||||
bool operator== (const sockaddr& addr1, const sockaddr& addr2) {
|
||||
return socketMatch(&addr1, &addr2);
|
||||
}
|
||||
|
||||
static sockaddr getZeroAddress() {
|
||||
sockaddr addr;
|
||||
memset(&addr, 0, sizeof(sockaddr));
|
||||
addr.sa_family = AF_INET;
|
||||
return addr;
|
||||
quint32 address = sockAddr.getAddress().toIPv4Address();
|
||||
return sockAddr.getPort() + qHash(QByteArray::fromRawData((char*) &address,
|
||||
sizeof(address)));
|
||||
}
|
||||
|
||||
Environment::Environment()
|
||||
|
@ -60,14 +49,14 @@ void Environment::init() {
|
|||
_skyFromSpaceProgram = createSkyProgram("Space", _skyFromSpaceUniformLocations);
|
||||
|
||||
// start off with a default-constructed environment data
|
||||
_data[getZeroAddress()][0];
|
||||
_data[HifiSockAddr()][0];
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void Environment::resetToDefault() {
|
||||
_data.clear();
|
||||
_data[getZeroAddress()][0];
|
||||
_data[HifiSockAddr()][0];
|
||||
}
|
||||
|
||||
void Environment::renderAtmospheres(Camera& camera) {
|
||||
|
@ -159,7 +148,7 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
|
|||
return found;
|
||||
}
|
||||
|
||||
int Environment::parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int numBytes) {
|
||||
int Environment::parseData(const HifiSockAddr& senderAddress, unsigned char* sourceBuffer, int numBytes) {
|
||||
// push past the packet header
|
||||
unsigned char* start = sourceBuffer;
|
||||
|
||||
|
@ -175,14 +164,14 @@ int Environment::parseData(sockaddr *senderAddress, unsigned char* sourceBuffer,
|
|||
int dataLength = newData.parseData(sourceBuffer, numBytes);
|
||||
|
||||
// update the mapping by address/ID
|
||||
_data[*senderAddress][newData.getID()] = newData;
|
||||
_data[senderAddress][newData.getID()] = newData;
|
||||
|
||||
sourceBuffer += dataLength;
|
||||
numBytes -= dataLength;
|
||||
}
|
||||
|
||||
// remove the default mapping, if any
|
||||
_data.remove(getZeroAddress());
|
||||
_data.remove(HifiSockAddr());
|
||||
|
||||
return sourceBuffer - start;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <QHash>
|
||||
#include <QMutex>
|
||||
|
||||
#include <UDPSocket.h>
|
||||
#include <HifiSockAddr.h>
|
||||
|
||||
#include "EnvironmentData.h"
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -34,7 +34,7 @@ public:
|
|||
|
||||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
|
||||
|
||||
int parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int numBytes);
|
||||
int parseData(const HifiSockAddr& senderSockAddr, unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -71,7 +71,7 @@ private:
|
|||
|
||||
typedef QHash<int, EnvironmentData> ServerData;
|
||||
|
||||
QHash<sockaddr, ServerData> _data;
|
||||
QHash<HifiSockAddr, ServerData> _data;
|
||||
|
||||
QMutex _mutex;
|
||||
};
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
#include "PairingHandler.h"
|
||||
|
@ -27,14 +30,14 @@ PairingHandler* PairingHandler::getInstance() {
|
|||
return instance;
|
||||
}
|
||||
|
||||
void PairingHandler::sendPairRequest() {
|
||||
// grab the node socket from the NodeList singleton
|
||||
UDPSocket *nodeSocket = NodeList::getInstance()->getNodeSocket();
|
||||
void PairingHandler::sendPairRequest() {
|
||||
|
||||
// prepare the pairing request packet
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
// use the getLocalAddress helper to get this client's listening address
|
||||
int localAddress = getLocalAddress();
|
||||
quint32 localAddress = htonl(getHostOrderLocalAddress());
|
||||
|
||||
char pairPacket[24] = {};
|
||||
sprintf(pairPacket, "Find %d.%d.%d.%d:%hu",
|
||||
|
@ -42,19 +45,13 @@ void PairingHandler::sendPairRequest() {
|
|||
(localAddress >> 8) & 0xFF,
|
||||
(localAddress >> 16) & 0xFF,
|
||||
(localAddress >> 24) & 0xFF,
|
||||
NodeList::getInstance()->getSocketListenPort());
|
||||
NodeList::getInstance()->getNodeSocket().localPort());
|
||||
|
||||
qDebug("Sending pair packet: %s\n", pairPacket);
|
||||
|
||||
sockaddr_in pairingServerSocket;
|
||||
|
||||
pairingServerSocket.sin_family = AF_INET;
|
||||
|
||||
// lookup the pairing server IP by the hostname
|
||||
struct hostent* hostInfo = gethostbyname(PAIRING_SERVER_HOSTNAME);
|
||||
memcpy(&pairingServerSocket.sin_addr, hostInfo->h_addr_list[0], hostInfo->h_length);
|
||||
pairingServerSocket.sin_port = htons(PAIRING_SERVER_PORT);
|
||||
HifiSockAddr pairingServerSocket(PAIRING_SERVER_HOSTNAME, PAIRING_SERVER_PORT);
|
||||
|
||||
// send the pair request to the pairing server
|
||||
nodeSocket->send((sockaddr*) &pairingServerSocket, pairPacket, strlen(pairPacket));
|
||||
nodeList->getNodeSocket().writeDatagram((char*) pairPacket, strlen(pairPacket),
|
||||
pairingServerSocket.getAddress(), pairingServerSocket.getPort());
|
||||
}
|
||||
|
|
46
interface/src/VoxelHideShowThread.cpp
Normal file
46
interface/src/VoxelHideShowThread.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// VoxelHideShowThread.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/1/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded interface thread for hiding and showing voxels in the local tree.
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
#include <NodeList.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Menu.h"
|
||||
#include "VoxelHideShowThread.h"
|
||||
|
||||
VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) :
|
||||
_theSystem(theSystem) {
|
||||
}
|
||||
|
||||
bool VoxelHideShowThread::process() {
|
||||
const uint64_t MSECS_TO_USECS = 1000;
|
||||
const uint64_t SECS_TO_USECS = 1000 * MSECS_TO_USECS;
|
||||
const uint64_t FRAME_RATE = 60;
|
||||
const uint64_t USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps
|
||||
|
||||
uint64_t start = usecTimestampNow();
|
||||
_theSystem->checkForCulling();
|
||||
uint64_t end = usecTimestampNow();
|
||||
uint64_t elapsed = end - start;
|
||||
|
||||
bool showExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
|
||||
if (showExtraDebugging && elapsed > USECS_PER_FRAME) {
|
||||
printf("VoxelHideShowThread::process()... checkForCulling took %llu\n", elapsed);
|
||||
}
|
||||
|
||||
if (isStillRunning()) {
|
||||
if (elapsed < USECS_PER_FRAME) {
|
||||
uint64_t sleepFor = USECS_PER_FRAME - elapsed;
|
||||
usleep(sleepFor);
|
||||
}
|
||||
}
|
||||
return isStillRunning(); // keep running till they terminate us
|
||||
}
|
31
interface/src/VoxelHideShowThread.h
Normal file
31
interface/src/VoxelHideShowThread.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// VoxelHideShowThread.h
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/1/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded voxel persistence
|
||||
//
|
||||
|
||||
#ifndef __interface__VoxelHideShowThread__
|
||||
#define __interface__VoxelHideShowThread__
|
||||
|
||||
#include <GenericThread.h>
|
||||
#include "VoxelSystem.h"
|
||||
|
||||
/// Generalized threaded processor for handling received inbound packets.
|
||||
class VoxelHideShowThread : public virtual GenericThread {
|
||||
public:
|
||||
|
||||
VoxelHideShowThread(VoxelSystem* theSystem);
|
||||
|
||||
protected:
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
|
||||
private:
|
||||
VoxelSystem* _theSystem;
|
||||
};
|
||||
|
||||
#endif // __interface__VoxelHideShowThread__
|
|
@ -14,7 +14,7 @@
|
|||
#include "Menu.h"
|
||||
#include "VoxelPacketProcessor.h"
|
||||
|
||||
void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"VoxelPacketProcessor::processPacket()");
|
||||
|
||||
|
@ -39,7 +39,7 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char*
|
|||
// then process any remaining bytes as if it was another packet
|
||||
if (packetData[0] == PACKET_TYPE_VOXEL_STATS) {
|
||||
|
||||
int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderAddress);
|
||||
int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderSockAddr);
|
||||
wasStatsPacket = true;
|
||||
if (messageLength > statsMessageLength) {
|
||||
packetData += statsMessageLength;
|
||||
|
@ -54,13 +54,12 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char*
|
|||
} // fall through to piggyback message
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
|
||||
app->trackIncomingVoxelPacket(packetData, messageLength, senderAddress, wasStatsPacket);
|
||||
app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket);
|
||||
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) {
|
||||
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (voxelServer && *voxelServer->getActiveSocket() == senderSockAddr) {
|
||||
if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
|
||||
app->_environment.parseData(&senderAddress, packetData, messageLength);
|
||||
app->_environment.parseData(senderSockAddr, packetData, messageLength);
|
||||
} else {
|
||||
app->_voxels.setDataSourceUUID(voxelServer->getUUID());
|
||||
app->_voxels.parseData(packetData, messageLength);
|
||||
|
|
|
@ -17,6 +17,6 @@
|
|||
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
|
||||
class VoxelPacketProcessor : public ReceivedPacketProcessor {
|
||||
protected:
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength);
|
||||
};
|
||||
#endif // __shared__VoxelPacketProcessor__
|
||||
|
|
|
@ -563,13 +563,15 @@ bool VoxelSystem::readFromSchematicFile(const char* filename) {
|
|||
}
|
||||
|
||||
int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
bool showTimingDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData()",showTimingDetails);
|
||||
|
||||
unsigned char command = *sourceBuffer;
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
|
||||
switch(command) {
|
||||
case PACKET_TYPE_VOXEL_DATA: {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"readBitstreamToTree()");
|
||||
|
||||
PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PACKET_TYPE_VOXEL_DATA part...",showTimingDetails);
|
||||
|
||||
unsigned char* dataAt = sourceBuffer + numBytesPacketHeader;
|
||||
|
||||
VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt));
|
||||
|
@ -611,7 +613,8 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
VoxelPacketData packetData(packetIsCompressed);
|
||||
packetData.loadFinalizedContent(dataAt, sectionLength);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
|
||||
qDebug("Got Packet color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
|
||||
qDebug("VoxelSystem::parseData() ... Got Packet Section"
|
||||
" color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
|
||||
" subsection:%d sectionLength:%d uncompressed:%d\n",
|
||||
debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed),
|
||||
sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize());
|
||||
|
@ -627,12 +630,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!_useFastVoxelPipeline || _writeRenderFullVBO) {
|
||||
setupNewVoxelsForDrawing();
|
||||
} else {
|
||||
checkForCulling();
|
||||
setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
|
||||
}
|
||||
|
||||
|
@ -661,8 +661,6 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
|
||||
_inSetupNewVoxelsForDrawing = true;
|
||||
|
||||
checkForCulling(); // check for out of view and deleted voxels...
|
||||
|
||||
bool didWriteFullVBO = _writeRenderFullVBO;
|
||||
if (_tree->isDirty()) {
|
||||
static char buffer[64] = { 0 };
|
||||
|
@ -747,68 +745,44 @@ void VoxelSystem::checkForCulling() {
|
|||
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()");
|
||||
uint64_t start = usecTimestampNow();
|
||||
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
|
||||
|
||||
// These items used to be menu options, we are not defaulting to and only supporting these modes.
|
||||
bool constantCulling = true;
|
||||
bool performHideOutOfViewLogic = true;
|
||||
bool performRemoveOutOfViewLogic = false;
|
||||
|
||||
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
|
||||
if (constantCulling || (
|
||||
(sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
|
||||
&& !isViewChanging()
|
||||
)
|
||||
) {
|
||||
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
|
||||
// them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which
|
||||
// can be expensive).
|
||||
if (performHideOutOfViewLogic) {
|
||||
|
||||
// track how long its been since we were last moving. If we have recently moved then only use delta frustums, if
|
||||
// it's been a long time since we last moved, then go ahead and do a full frustum cull.
|
||||
if (isViewChanging()) {
|
||||
_lastViewIsChanging = start;
|
||||
}
|
||||
uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000;
|
||||
|
||||
bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS));
|
||||
|
||||
// These has changed events will occur before we stop. So we need to remember this for when we finally have stopped
|
||||
// moving long enough to be enoughTime
|
||||
if (hasViewChanged()) {
|
||||
_hasRecentlyChanged = true;
|
||||
}
|
||||
|
||||
// If we have recently changed, but it's been enough time since we last moved, then we will do a full frustum
|
||||
// hide/show culling pass
|
||||
bool forceFullFrustum = enoughTime && _hasRecentlyChanged;
|
||||
|
||||
// in hide mode, we only track the full frustum culls, because we don't care about the partials.
|
||||
if (forceFullFrustum) {
|
||||
_lastViewCulling = start;
|
||||
_hasRecentlyChanged = false;
|
||||
}
|
||||
|
||||
hideOutOfView(forceFullFrustum);
|
||||
|
||||
if (forceFullFrustum) {
|
||||
uint64_t endViewCulling = usecTimestampNow();
|
||||
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
|
||||
}
|
||||
|
||||
} else if (performRemoveOutOfViewLogic) {
|
||||
_lastViewCulling = start;
|
||||
removeOutOfView();
|
||||
uint64_t endViewCulling = usecTimestampNow();
|
||||
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
|
||||
}
|
||||
|
||||
// Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So,
|
||||
// we should consider putting this someplace else... as this might be able to occur less frequently, and save us on
|
||||
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
|
||||
cleanupRemovedVoxels();
|
||||
// track how long its been since we were last moving. If we have recently moved then only use delta frustums, if
|
||||
// it's been a long time since we last moved, then go ahead and do a full frustum cull.
|
||||
if (isViewChanging()) {
|
||||
_lastViewIsChanging = start;
|
||||
}
|
||||
uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000;
|
||||
|
||||
bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS));
|
||||
|
||||
// These has changed events will occur before we stop. So we need to remember this for when we finally have stopped
|
||||
// moving long enough to be enoughTime
|
||||
if (hasViewChanged()) {
|
||||
_hasRecentlyChanged = true;
|
||||
}
|
||||
|
||||
// If we have recently changed, but it's been enough time since we last moved, then we will do a full frustum
|
||||
// hide/show culling pass
|
||||
bool forceFullFrustum = enoughTime && _hasRecentlyChanged;
|
||||
|
||||
// in hide mode, we only track the full frustum culls, because we don't care about the partials.
|
||||
if (forceFullFrustum) {
|
||||
_lastViewCulling = start;
|
||||
_hasRecentlyChanged = false;
|
||||
}
|
||||
|
||||
hideOutOfView(forceFullFrustum);
|
||||
|
||||
if (forceFullFrustum) {
|
||||
uint64_t endViewCulling = usecTimestampNow();
|
||||
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
|
||||
}
|
||||
|
||||
// Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So,
|
||||
// we should consider putting this someplace else... as this might be able to occur less frequently, and save us on
|
||||
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
|
||||
cleanupRemovedVoxels();
|
||||
|
||||
uint64_t sinceLastAudit = (start - _lastAudit) / 1000;
|
||||
|
||||
|
@ -1456,9 +1430,9 @@ void VoxelSystem::clearAllNodesBufferIndex() {
|
|||
}
|
||||
}
|
||||
|
||||
bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* node, void* extraData) {
|
||||
bool VoxelSystem::forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData) {
|
||||
_nodeCount++;
|
||||
node->setDirtyBit();
|
||||
element->setDirtyBit();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1542,6 +1516,39 @@ void VoxelSystem::falseColorizeInView() {
|
|||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
class VoxelAndPoint {
|
||||
public:
|
||||
VoxelTreeElement* voxel;
|
||||
glm::vec3 point;
|
||||
};
|
||||
|
||||
// Find the smallest colored voxel enclosing a point (if there is one)
|
||||
bool VoxelSystem::getVoxelEnclosingOperation(OctreeElement* element, void* extraData) {
|
||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
||||
VoxelAndPoint* args = (VoxelAndPoint*) extraData;
|
||||
AABox voxelBox = voxel->getAABox();
|
||||
if (voxelBox.contains(args->point)) {
|
||||
if (voxel->isColored() && voxel->isLeaf()) {
|
||||
// we've reached a solid leaf containing the point, return the node.
|
||||
args->voxel = voxel;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// The point is not inside this voxel, so stop recursing.
|
||||
return false;
|
||||
}
|
||||
return true; // keep looking
|
||||
}
|
||||
|
||||
VoxelTreeElement* VoxelSystem::getVoxelEnclosing(const glm::vec3& point) {
|
||||
VoxelAndPoint voxelAndPoint;
|
||||
voxelAndPoint.point = point;
|
||||
voxelAndPoint.voxel = NULL;
|
||||
_tree->recurseTreeWithOperation(getVoxelEnclosingOperation, (void*) &voxelAndPoint);
|
||||
return voxelAndPoint.voxel;
|
||||
}
|
||||
|
||||
|
||||
// helper classes and args for falseColorizeBySource
|
||||
class groupColor {
|
||||
public:
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <glm/glm.hpp>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <UDPSocket.h>
|
||||
|
||||
#include <CoverageMapV2.h>
|
||||
#include <NodeData.h>
|
||||
|
@ -40,6 +39,9 @@ struct VoxelShaderVBOData
|
|||
class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook,
|
||||
public NodeListHook, public DomainChangeListener {
|
||||
Q_OBJECT
|
||||
|
||||
friend class VoxelHideShowThread;
|
||||
|
||||
public:
|
||||
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM);
|
||||
~VoxelSystem();
|
||||
|
@ -114,6 +116,8 @@ public:
|
|||
virtual void domainChanged(QString domain);
|
||||
|
||||
bool treeIsBusy() const { return _treeIsBusy; }
|
||||
|
||||
VoxelTreeElement* getVoxelEnclosing(const glm::vec3& point);
|
||||
|
||||
signals:
|
||||
void importSize(float x, float y, float z);
|
||||
|
@ -173,26 +177,27 @@ private:
|
|||
|
||||
// Operation functions for tree recursion methods
|
||||
static int _nodeCount;
|
||||
static bool randomColorOperation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeRandomOperation(OctreeElement* node, void* extraData);
|
||||
static bool trueColorizeOperation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeInViewOperation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeDistanceFromViewOperation(OctreeElement* node, void* extraData);
|
||||
static bool getDistanceFromViewRangeOperation(OctreeElement* node, void* extraData);
|
||||
static bool removeOutOfViewOperation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeRandomEveryOtherOperation(OctreeElement* node, void* extraData);
|
||||
static bool collectStatsForTreesAndVBOsOperation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeOccludedOperation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeSubTreeOperation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeOccludedV2Operation(OctreeElement* node, void* extraData);
|
||||
static bool falseColorizeBySourceOperation(OctreeElement* node, void* extraData);
|
||||
static bool killSourceVoxelsOperation(OctreeElement* node, void* extraData);
|
||||
static bool forceRedrawEntireTreeOperation(OctreeElement* node, void* extraData);
|
||||
static bool clearAllNodesBufferIndexOperation(OctreeElement* node, void* extraData);
|
||||
static bool hideOutOfViewOperation(OctreeElement* node, void* extraData);
|
||||
static bool hideAllSubTreeOperation(OctreeElement* node, void* extraData);
|
||||
static bool showAllSubTreeOperation(OctreeElement* node, void* extraData);
|
||||
static bool showAllLocalVoxelsOperation(OctreeElement* node, void* extraData);
|
||||
static bool randomColorOperation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeRandomOperation(OctreeElement* element, void* extraData);
|
||||
static bool trueColorizeOperation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeInViewOperation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeDistanceFromViewOperation(OctreeElement* element, void* extraData);
|
||||
static bool getDistanceFromViewRangeOperation(OctreeElement* element, void* extraData);
|
||||
static bool removeOutOfViewOperation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeRandomEveryOtherOperation(OctreeElement* element, void* extraData);
|
||||
static bool collectStatsForTreesAndVBOsOperation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeOccludedOperation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeSubTreeOperation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeOccludedV2Operation(OctreeElement* element, void* extraData);
|
||||
static bool falseColorizeBySourceOperation(OctreeElement* element, void* extraData);
|
||||
static bool killSourceVoxelsOperation(OctreeElement* element, void* extraData);
|
||||
static bool forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData);
|
||||
static bool clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData);
|
||||
static bool hideOutOfViewOperation(OctreeElement* element, void* extraData);
|
||||
static bool hideAllSubTreeOperation(OctreeElement* element, void* extraData);
|
||||
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
|
||||
static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData);
|
||||
static bool getVoxelEnclosingOperation(OctreeElement* element, void* extraData);
|
||||
|
||||
int updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, bool forceDraw);
|
||||
int forceRemoveNodeFromArrays(VoxelTreeElement* node);
|
||||
|
|
|
@ -24,7 +24,10 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
_raveGloveInitialized(false),
|
||||
_owningAvatar(owningAvatar),
|
||||
_renderAlpha(1.0),
|
||||
_ballColor(0.0, 0.0, 0.4)
|
||||
_ballColor(0.0, 0.0, 0.4),
|
||||
_collisionCenter(0,0,0),
|
||||
_collisionAge(0),
|
||||
_collisionDuration(0)
|
||||
{
|
||||
// initialize all finger particle emitters with an invalid id as default
|
||||
for (int f = 0; f< NUM_FINGERS; f ++ ) {
|
||||
|
@ -51,6 +54,10 @@ void Hand::reset() {
|
|||
|
||||
void Hand::simulate(float deltaTime, bool isMine) {
|
||||
|
||||
if (_collisionAge > 0.f) {
|
||||
_collisionAge += deltaTime;
|
||||
}
|
||||
|
||||
calculateGeometry();
|
||||
|
||||
if (_isRaveGloveActive) {
|
||||
|
@ -67,29 +74,77 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
FingerData& finger = palm.getFingers()[0];
|
||||
glm::vec3 newVoxelPosition = finger.getTipPosition();
|
||||
FingerData& finger = palm.getFingers()[0]; // Sixense has only one finger
|
||||
glm::vec3 fingerTipPosition = finger.getTipPosition();
|
||||
if (palm.getControllerButtons() & BUTTON_1) {
|
||||
if (glm::length(newVoxelPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
|
||||
if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
|
||||
QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value<QColor>();
|
||||
Application::getInstance()->makeVoxel(newVoxelPosition,
|
||||
Application::getInstance()->makeVoxel(fingerTipPosition,
|
||||
FINGERTIP_VOXEL_SIZE,
|
||||
paintColor.red(),
|
||||
paintColor.green(),
|
||||
paintColor.blue(),
|
||||
true);
|
||||
_lastFingerAddVoxel = newVoxelPosition;
|
||||
_lastFingerAddVoxel = fingerTipPosition;
|
||||
}
|
||||
} else if (palm.getControllerButtons() & BUTTON_2) {
|
||||
if (glm::length(newVoxelPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
|
||||
Application::getInstance()->removeVoxel(newVoxelPosition, FINGERTIP_VOXEL_SIZE);
|
||||
_lastFingerDeleteVoxel = newVoxelPosition;
|
||||
if (glm::length(fingerTipPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
|
||||
Application::getInstance()->removeVoxel(fingerTipPosition, FINGERTIP_VOXEL_SIZE);
|
||||
_lastFingerDeleteVoxel = fingerTipPosition;
|
||||
}
|
||||
}
|
||||
// Check if the finger is intersecting with a voxel in the client voxel tree
|
||||
VoxelTreeElement* fingerNode = Application::getInstance()->getVoxels()->getVoxelEnclosing(
|
||||
glm::vec3(fingerTipPosition / (float)TREE_SCALE));
|
||||
if (fingerNode) {
|
||||
if (!palm.getIsCollidingWithVoxel()) {
|
||||
// Collision has just started
|
||||
palm.setIsCollidingWithVoxel(true);
|
||||
handleVoxelCollision(&palm, fingerTipPosition, fingerNode, deltaTime);
|
||||
// Set highlight voxel
|
||||
VoxelDetail voxel;
|
||||
glm::vec3 pos = fingerNode->getCorner();
|
||||
voxel.x = pos.x;
|
||||
voxel.y = pos.y;
|
||||
voxel.z = pos.z;
|
||||
voxel.s = fingerNode->getScale();
|
||||
voxel.red = fingerNode->getColor()[0];
|
||||
voxel.green = fingerNode->getColor()[1];
|
||||
voxel.blue = fingerNode->getColor()[2];
|
||||
Application::getInstance()->setHighlightVoxel(voxel);
|
||||
Application::getInstance()->setIsHighlightVoxel(true);
|
||||
}
|
||||
} else {
|
||||
if (palm.getIsCollidingWithVoxel()) {
|
||||
// Collision has just ended
|
||||
palm.setIsCollidingWithVoxel(false);
|
||||
Application::getInstance()->setIsHighlightVoxel(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime) {
|
||||
//
|
||||
// Collision between finger and a voxel plays sound
|
||||
//
|
||||
float volume = glm::length(palm->getVelocity());
|
||||
float duration = volume;
|
||||
_collisionCenter = fingerTipPosition;
|
||||
_collisionAge = deltaTime;
|
||||
_collisionDuration = duration;
|
||||
int voxelBrightness = voxel->getColor()[0] + voxel->getColor()[1] + voxel->getColor()[2];
|
||||
float frequency = 100.f + (voxelBrightness * 2.f); // Hz
|
||||
// Play a sound
|
||||
Application::getInstance()->getAudio()->startCollisionSound(volume,
|
||||
frequency,
|
||||
0.25,
|
||||
0.995f,
|
||||
false);
|
||||
|
||||
}
|
||||
|
||||
void Hand::calculateGeometry() {
|
||||
const glm::vec3 leapHandsOffsetFromFace(0.0, -0.2, -0.3); // place the hand in front of the face where we can see it
|
||||
|
||||
|
@ -105,7 +160,7 @@ void Hand::calculateGeometry() {
|
|||
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
|
||||
FingerData& finger = palm.getFingers()[f];
|
||||
if (finger.isActive()) {
|
||||
const float standardBallRadius = 0.005f;
|
||||
const float standardBallRadius = 0.010f;
|
||||
_leapFingerTipBalls.resize(_leapFingerTipBalls.size() + 1);
|
||||
HandBall& ball = _leapFingerTipBalls.back();
|
||||
ball.rotation = _baseOrientation;
|
||||
|
@ -113,6 +168,7 @@ void Hand::calculateGeometry() {
|
|||
ball.radius = standardBallRadius;
|
||||
ball.touchForce = 0.0;
|
||||
ball.isCollidable = true;
|
||||
ball.isColliding = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +190,7 @@ void Hand::calculateGeometry() {
|
|||
ball.radius = standardBallRadius;
|
||||
ball.touchForce = 0.0;
|
||||
ball.isCollidable = true;
|
||||
ball.isColliding = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,6 +241,21 @@ void Hand::render() {
|
|||
}
|
||||
}
|
||||
|
||||
// If hand/voxel collision has happened, render a little expanding sphere
|
||||
if (_collisionAge > 0.f) {
|
||||
float opacity = glm::clamp(1.f - (_collisionAge / _collisionDuration), 0.f, 1.f);
|
||||
glColor4f(1, 0, 0, 0.5 * opacity);
|
||||
glPushMatrix();
|
||||
glTranslatef(_collisionCenter.x, _collisionCenter.y, _collisionCenter.z);
|
||||
glutSolidSphere(_collisionAge * 0.25f, 20, 20);
|
||||
glPopMatrix();
|
||||
if (_collisionAge > _collisionDuration) {
|
||||
_collisionAge = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If hand controller buttons pressed, render stuff as needed
|
||||
if (getPalms().size() > 0) {
|
||||
for (size_t i = 0; i < getPalms().size(); ++i) {
|
||||
|
@ -299,8 +371,11 @@ void Hand::renderLeapHands() {
|
|||
// Draw the leap balls
|
||||
for (size_t i = 0; i < _leapFingerTipBalls.size(); i++) {
|
||||
if (alpha > 0.0f) {
|
||||
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
|
||||
|
||||
if (_leapFingerTipBalls[i].isColliding) {
|
||||
glColor4f(handColor.r, 0, 0, alpha);
|
||||
} else {
|
||||
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
|
||||
}
|
||||
glPushMatrix();
|
||||
glTranslatef(_leapFingerTipBalls[i].position.x, _leapFingerTipBalls[i].position.y, _leapFingerTipBalls[i].position.z);
|
||||
glutSolidSphere(_leapFingerTipBalls[i].radius, 20.0f, 20.0f);
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
glm::vec3 velocity; // the velocity of the ball
|
||||
float radius; // the radius of the ball
|
||||
bool isCollidable; // whether or not the ball responds to collisions
|
||||
bool isColliding; // ball is currently colliding
|
||||
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
|
||||
};
|
||||
|
||||
|
@ -83,6 +84,12 @@ private:
|
|||
std::vector<HandBall> _leapFingerRootBalls;
|
||||
|
||||
glm::vec3 _lastFingerAddVoxel, _lastFingerDeleteVoxel;
|
||||
bool _isCollidingWithVoxel;
|
||||
VoxelDetail _collidingVoxel;
|
||||
|
||||
glm::vec3 _collisionCenter;
|
||||
float _collisionAge;
|
||||
float _collisionDuration;
|
||||
|
||||
// private methods
|
||||
void setLeapHands(const std::vector<glm::vec3>& handPositions,
|
||||
|
@ -93,6 +100,8 @@ private:
|
|||
void renderLeapHands();
|
||||
void renderLeapFingerTrails();
|
||||
void calculateGeometry();
|
||||
|
||||
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
//
|
||||
// HandControl.cpp
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "HandControl.h"
|
||||
|
||||
// this class takes mouse movements normalized within the screen
|
||||
// dimensions and uses those to determine avatar hand movements, as well
|
||||
// as states for ramping up and ramping down the amplitude of such movements.
|
||||
//
|
||||
// This class might expand to accommodate 3D input devices
|
||||
//
|
||||
|
||||
HandControl::HandControl() {
|
||||
|
||||
_enabled = false;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
_startX = 0;
|
||||
_startY = 0;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
_lastX = 0;
|
||||
_lastY = 0;
|
||||
_velocityX = 0;
|
||||
_velocityY = 0;
|
||||
_rampUpRate = 0.05;
|
||||
_rampDownRate = 0.02;
|
||||
_envelope = 0.0f;
|
||||
}
|
||||
|
||||
void HandControl::setScreenDimensions(int width, int height) {
|
||||
_width = width;
|
||||
_height = height;
|
||||
_startX = _width / 2;
|
||||
_startY = _height / 2;
|
||||
}
|
||||
|
||||
void HandControl::update(int x, int y) {
|
||||
_lastX = _x;
|
||||
_lastY = _y;
|
||||
_x = x;
|
||||
_y = y;
|
||||
_velocityX = _x - _lastX;
|
||||
_velocityY = _y - _lastY;
|
||||
|
||||
// if the mouse is moving, ramp up the envelope to increase amplitude of hand movement...
|
||||
if ((_velocityX != 0)
|
||||
|| (_velocityY != 0)) {
|
||||
_enabled = true;
|
||||
if (_envelope < 1.0) {
|
||||
_envelope += _rampUpRate;
|
||||
if (_envelope >= 1.0) {
|
||||
_envelope = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if not enabled ramp down the envelope to decrease amplitude of hand movement...
|
||||
if (! _enabled) {
|
||||
if (_envelope > 0.0) {
|
||||
_envelope -= _rampDownRate;
|
||||
if (_envelope <= 0.0) {
|
||||
_startX = _width / 2;
|
||||
_startY = _height / 2;
|
||||
_envelope = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_leftRight = 0.0;
|
||||
_downUp = 0.0;
|
||||
_backFront = 0.0;
|
||||
|
||||
// if envelope is greater than zero, apply mouse movement to values to be output
|
||||
if (_envelope > 0.0) {
|
||||
_leftRight += ((_x - _startX) / (float)_width ) * _envelope;
|
||||
_downUp += ((_y - _startY) / (float)_height) * _envelope;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 HandControl::getValues() {
|
||||
return glm::vec3(_leftRight, _downUp, _backFront);
|
||||
}
|
||||
|
||||
void HandControl::stop() {
|
||||
_enabled = false;
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
//
|
||||
// HandControl.h
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__HandControl__
|
||||
#define __interface__HandControl__
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class HandControl {
|
||||
public:
|
||||
HandControl();
|
||||
void setScreenDimensions(int width, int height);
|
||||
void update( int x, int y );
|
||||
glm::vec3 getValues();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
bool _enabled;
|
||||
int _width;
|
||||
int _height;
|
||||
int _startX;
|
||||
int _startY;
|
||||
int _x;
|
||||
int _y;
|
||||
int _lastX;
|
||||
int _lastY;
|
||||
int _velocityX;
|
||||
int _velocityY;
|
||||
float _rampUpRate;
|
||||
float _rampDownRate;
|
||||
float _envelope;
|
||||
float _leftRight;
|
||||
float _downUp;
|
||||
float _backFront;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -32,7 +32,6 @@ void SixenseManager::update(float deltaTime) {
|
|||
}
|
||||
MyAvatar* avatar = Application::getInstance()->getAvatar();
|
||||
Hand& hand = avatar->getHand();
|
||||
hand.getPalms().clear();
|
||||
|
||||
int maxControllers = sixenseGetMaxControllers();
|
||||
for (int i = 0; i < maxControllers; i++) {
|
||||
|
@ -42,45 +41,63 @@ void SixenseManager::update(float deltaTime) {
|
|||
sixenseControllerData data;
|
||||
sixenseGetNewestData(i, &data);
|
||||
|
||||
// Set palm position and normal based on Hydra position/orientation
|
||||
PalmData palm(&hand);
|
||||
palm.setActive(true);
|
||||
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
|
||||
//printf("si: %i\n", data.controller_index);
|
||||
|
||||
// Compute current velocity from position change
|
||||
palm.setVelocity((position - palm.getPosition()) / deltaTime);
|
||||
// Set palm position and normal based on Hydra position/orientation
|
||||
|
||||
// Either find a palm matching the sixense controller, or make a new one
|
||||
PalmData* palm;
|
||||
bool foundHand = false;
|
||||
for (int j = 0; j < hand.getNumPalms(); j++) {
|
||||
if (hand.getPalms()[j].getSixenseID() == data.controller_index) {
|
||||
palm = &hand.getPalms()[j];
|
||||
foundHand = true;
|
||||
}
|
||||
}
|
||||
if (!foundHand) {
|
||||
PalmData newPalm(&hand);
|
||||
hand.getPalms().push_back(newPalm);
|
||||
palm = &hand.getPalms()[hand.getNumPalms() - 1];
|
||||
palm->setSixenseID(data.controller_index);
|
||||
printf("Found new Sixense controller, ID %i\n", data.controller_index);
|
||||
}
|
||||
|
||||
palm->setActive(true);
|
||||
|
||||
// Read controller buttons and joystick into the hand
|
||||
palm.setControllerButtons(data.buttons);
|
||||
palm.setTrigger(data.trigger);
|
||||
palm.setJoystick(data.joystick_x, data.joystick_y);
|
||||
|
||||
palm->setControllerButtons(data.buttons);
|
||||
palm->setTrigger(data.trigger);
|
||||
palm->setJoystick(data.joystick_x, data.joystick_y);
|
||||
|
||||
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
|
||||
// Adjust for distance between acquisition 'orb' and the user's torso
|
||||
// (distance to the right of body center, distance below torso, distance behind torso)
|
||||
const glm::vec3 SPHERE_TO_TORSO(-250.f, -300.f, -300.f);
|
||||
position = SPHERE_TO_TORSO + position;
|
||||
palm.setRawPosition(position);
|
||||
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]);
|
||||
|
||||
// Rotate about controller
|
||||
// Compute current velocity from position change
|
||||
palm->setVelocity((position - palm->getRawPosition()) / deltaTime / 1000.f); // meters/sec
|
||||
palm->setRawPosition(position);
|
||||
|
||||
// Rotation of Palm
|
||||
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]);
|
||||
rotation = glm::angleAxis(180.0f, 0.f, 1.f, 0.f) * rotation;
|
||||
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
|
||||
palm.setRawNormal(rotation * PALM_VECTOR);
|
||||
palm->setRawNormal(rotation * PALM_VECTOR);
|
||||
|
||||
// initialize the "finger" based on the direction
|
||||
FingerData finger(&palm, &hand);
|
||||
FingerData finger(palm, &hand);
|
||||
finger.setActive(true);
|
||||
finger.setRawRootPosition(position);
|
||||
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, 100.0f);
|
||||
const float FINGER_LENGTH = 300.0f; // Millimeters
|
||||
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
|
||||
finger.setRawTipPosition(position + rotation * FINGER_VECTOR);
|
||||
|
||||
// three fingers indicates to the skeleton that we have enough data to determine direction
|
||||
palm.getFingers().clear();
|
||||
palm.getFingers().push_back(finger);
|
||||
palm.getFingers().push_back(finger);
|
||||
palm.getFingers().push_back(finger);
|
||||
|
||||
hand.getPalms().push_back(palm);
|
||||
palm->getFingers().clear();
|
||||
palm->getFingers().push_back(finger);
|
||||
palm->getFingers().push_back(finger);
|
||||
palm->getFingers().push_back(finger);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -24,22 +24,24 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit,
|
|||
vertices->clear();
|
||||
vertices->reserve(limit);
|
||||
|
||||
const unsigned MILKY_WAY_WIDTH = 16.0; // width in degrees of one half of the Milky Way
|
||||
const float MILKY_WAY_INCLINATION = 30.0f; // angle of Milky Way from horizontal in degrees
|
||||
const float MILKY_WAY_RATIO = 0.6;
|
||||
const unsigned MILKY_WAY_WIDTH = 12.0; // width in degrees of one half of the Milky Way
|
||||
const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees
|
||||
const float MILKY_WAY_RATIO = 0.4;
|
||||
const unsigned NUM_DEGREES = 360;
|
||||
|
||||
for(int star = 0; star < floor(limit * (1 - MILKY_WAY_RATIO)); ++star) {
|
||||
float azimuth, altitude;
|
||||
azimuth = (((float)rand() / (float) RAND_MAX) * NUM_DEGREES) - (NUM_DEGREES / 2);
|
||||
altitude = (acos((2.0f * ((float)rand() / (float)RAND_MAX)) - 1.0f) / PI_OVER_180) + 90;
|
||||
|
||||
vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION)));
|
||||
}
|
||||
|
||||
for(int star = 0; star < ceil(limit * MILKY_WAY_RATIO); ++star) {
|
||||
float azimuth = ((float)rand() / (float) RAND_MAX) * NUM_DEGREES;
|
||||
float altitude = asin((float)rand() / (float) RAND_MAX * 2 - 1) * MILKY_WAY_WIDTH;
|
||||
float altitude = powf(randFloat()*0.5f, 2.f)/0.25f * MILKY_WAY_WIDTH;
|
||||
if (randFloat() > 0.5f) {
|
||||
altitude *= -1.f;
|
||||
}
|
||||
|
||||
// we need to rotate the Milky Way band to the correct orientation in the sky
|
||||
// convert from spherical coordinates to cartesian, rotate the point and then convert back.
|
||||
|
@ -71,8 +73,15 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit,
|
|||
// 0 = completely black & white
|
||||
// 1 = very colorful
|
||||
unsigned Generator::computeStarColor(float colorization) {
|
||||
unsigned char red = rand() % 256;
|
||||
unsigned char green = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
unsigned char blue = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
return red | green << 8 | blue << 16;
|
||||
unsigned char red, green, blue;
|
||||
if (randFloat() < 0.3f) {
|
||||
// A few stars are colorful
|
||||
red = 2 + (rand() % 254);
|
||||
green = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
|
||||
blue = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
|
||||
} else {
|
||||
// Most stars are dimmer and white
|
||||
red = green = blue = 2 + (rand() % 128);
|
||||
}
|
||||
return red | (green << 8) | (blue << 16);
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
//
|
||||
// AudioInjectionManager.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 5/16/13.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "SharedUtil.h"
|
||||
#include "NodeList.h"
|
||||
#include "NodeTypes.h"
|
||||
#include "Node.h"
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
#include "AudioInjectionManager.h"
|
||||
|
||||
UDPSocket* AudioInjectionManager::_injectorSocket = NULL;
|
||||
sockaddr AudioInjectionManager::_destinationSocket;
|
||||
bool AudioInjectionManager::_isDestinationSocketExplicit = false;
|
||||
AudioInjector* AudioInjectionManager::_injectors[50] = {};
|
||||
|
||||
AudioInjector* AudioInjectionManager::injectorWithCapacity(int capacity) {
|
||||
for (int i = 0; i < MAX_CONCURRENT_INJECTORS; i++) {
|
||||
if (!_injectors[i]) {
|
||||
_injectors[i] = new AudioInjector(capacity);
|
||||
return _injectors[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AudioInjectionManager::setDestinationSocket(sockaddr& destinationSocket) {
|
||||
_destinationSocket = destinationSocket;
|
||||
_isDestinationSocketExplicit = true;
|
||||
}
|
||||
|
||||
void* AudioInjectionManager::injectAudioViaThread(void* args) {
|
||||
AudioInjector* injector = (AudioInjector*) args;
|
||||
|
||||
// if we don't have an injectorSocket then grab the one from the node list
|
||||
if (!_injectorSocket) {
|
||||
_injectorSocket = NodeList::getInstance()->getNodeSocket();
|
||||
}
|
||||
|
||||
// if we don't have an explicit destination socket then pull active socket for current audio mixer from node list
|
||||
if (!_isDestinationSocketExplicit) {
|
||||
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
|
||||
if (audioMixer && audioMixer->getActiveSocket()) {
|
||||
_destinationSocket = *audioMixer->getActiveSocket();
|
||||
} else {
|
||||
pthread_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
injector->injectAudio(_injectorSocket, &_destinationSocket);
|
||||
|
||||
// if this an injector inside the injection manager's array we're responsible for deletion
|
||||
for (int i = 0; i < MAX_CONCURRENT_INJECTORS; i++) {
|
||||
if (_injectors[i] == injector) {
|
||||
// pointer matched - delete this injector
|
||||
delete injector;
|
||||
|
||||
// set the pointer to NULL so we can reuse this spot
|
||||
_injectors[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
void AudioInjectionManager::threadInjector(AudioInjector* injector) {
|
||||
pthread_t audioInjectThread;
|
||||
pthread_create(&audioInjectThread, NULL, injectAudioViaThread, (void*) injector);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// AudioInjectionManager.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 5/16/13.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AudioInjectionManager__
|
||||
#define __hifi__AudioInjectionManager__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "UDPSocket.h"
|
||||
#include "AudioInjector.h"
|
||||
|
||||
const int MAX_CONCURRENT_INJECTORS = 50;
|
||||
|
||||
class AudioInjectionManager {
|
||||
public:
|
||||
static AudioInjector* injectorWithCapacity(int capacity);
|
||||
|
||||
static void threadInjector(AudioInjector* injector);
|
||||
|
||||
static void setInjectorSocket(UDPSocket* injectorSocket) { _injectorSocket = injectorSocket;}
|
||||
static void setDestinationSocket(sockaddr& destinationSocket);
|
||||
private:
|
||||
static void* injectAudioViaThread(void* args);
|
||||
|
||||
static UDPSocket* _injectorSocket;
|
||||
static sockaddr _destinationSocket;
|
||||
static bool _isDestinationSocketExplicit;
|
||||
static AudioInjector* _injectors[MAX_CONCURRENT_INJECTORS];
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AudioInjectionManager__) */
|
|
@ -1,137 +0,0 @@
|
|||
//
|
||||
// AudioInjector.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 4/23/13.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "AudioInjector.h"
|
||||
|
||||
AudioInjector::AudioInjector(int maxNumSamples) :
|
||||
_streamIdentifier(QUuid::createUuid()),
|
||||
_numTotalSamples(maxNumSamples),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_orientation(),
|
||||
_radius(0.0f),
|
||||
_volume(MAX_INJECTOR_VOLUME),
|
||||
_indexOfNextSlot(0),
|
||||
_isInjectingAudio(false)
|
||||
{
|
||||
_audioSampleArray = new int16_t[maxNumSamples];
|
||||
memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
AudioInjector::~AudioInjector() {
|
||||
delete[] _audioSampleArray;
|
||||
}
|
||||
|
||||
void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destinationSocket) {
|
||||
if (_audioSampleArray && _indexOfNextSlot > 0) {
|
||||
_isInjectingAudio = true;
|
||||
|
||||
timeval startTime;
|
||||
|
||||
// calculate the number of bytes required for additional data
|
||||
int leadingBytes = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_INJECT_AUDIO)
|
||||
+ NUM_BYTES_RFC4122_UUID
|
||||
+ NUM_BYTES_RFC4122_UUID
|
||||
+ sizeof(_position)
|
||||
+ sizeof(_orientation)
|
||||
+ sizeof(_radius)
|
||||
+ sizeof(_volume);
|
||||
|
||||
unsigned char dataPacket[(BUFFER_LENGTH_SAMPLES_PER_CHANNEL * sizeof(int16_t)) + leadingBytes];
|
||||
|
||||
unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, PACKET_TYPE_INJECT_AUDIO);
|
||||
|
||||
// copy the UUID for the owning node
|
||||
QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122();
|
||||
memcpy(currentPacketPtr, rfcUUID.constData(), rfcUUID.size());
|
||||
currentPacketPtr += rfcUUID.size();
|
||||
|
||||
// copy the stream identifier
|
||||
QByteArray rfcStreamIdentifier = _streamIdentifier.toRfc4122();
|
||||
memcpy(currentPacketPtr, rfcStreamIdentifier.constData(), rfcStreamIdentifier.size());
|
||||
currentPacketPtr += rfcStreamIdentifier.size();
|
||||
|
||||
memcpy(currentPacketPtr, &_position, sizeof(_position));
|
||||
currentPacketPtr += sizeof(_position);
|
||||
|
||||
memcpy(currentPacketPtr, &_orientation, sizeof(_orientation));
|
||||
currentPacketPtr += sizeof(_orientation);
|
||||
|
||||
memcpy(currentPacketPtr, &_radius, sizeof(_radius));
|
||||
currentPacketPtr += sizeof(_radius);
|
||||
|
||||
*currentPacketPtr = _volume;
|
||||
currentPacketPtr++;
|
||||
|
||||
gettimeofday(&startTime, NULL);
|
||||
int nextFrame = 0;
|
||||
|
||||
for (int i = 0; i < _numTotalSamples; i += BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
int usecToSleep = usecTimestamp(&startTime) + (nextFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow();
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
||||
int numSamplesToCopy = BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
|
||||
if (_numTotalSamples - i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
|
||||
numSamplesToCopy = _numTotalSamples - i;
|
||||
memset(currentPacketPtr + numSamplesToCopy,
|
||||
0,
|
||||
BUFFER_LENGTH_BYTES_PER_CHANNEL - (numSamplesToCopy * sizeof(int16_t)));
|
||||
}
|
||||
|
||||
memcpy(currentPacketPtr, _audioSampleArray + i, numSamplesToCopy * sizeof(int16_t));
|
||||
|
||||
injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket));
|
||||
}
|
||||
|
||||
_isInjectingAudio = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInjector::addSample(const int16_t sample) {
|
||||
if (_indexOfNextSlot != _numTotalSamples) {
|
||||
// only add this sample if we actually have space for it
|
||||
_audioSampleArray[_indexOfNextSlot++] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInjector::addSamples(int16_t* sampleBuffer, int numSamples) {
|
||||
if (_audioSampleArray + _indexOfNextSlot + numSamples <= _audioSampleArray + _numTotalSamples) {
|
||||
// only copy the audio from the sample buffer if there's space
|
||||
memcpy(_audioSampleArray + _indexOfNextSlot, sampleBuffer, numSamples * sizeof(int16_t));
|
||||
_indexOfNextSlot += numSamples;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInjector::clear() {
|
||||
_indexOfNextSlot = 0;
|
||||
memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
int16_t& AudioInjector::sampleAt(const int index) {
|
||||
assert(index >= 0 && index < _numTotalSamples);
|
||||
|
||||
return _audioSampleArray[index];
|
||||
}
|
||||
|
||||
void AudioInjector::insertSample(const int index, int sample) {
|
||||
assert (index >= 0 && index < _numTotalSamples);
|
||||
|
||||
_audioSampleArray[index] = (int16_t) sample;
|
||||
_indexOfNextSlot = index + 1;
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// AudioInjector.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 4/23/13.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AudioInjector__
|
||||
#define __hifi__AudioInjector__
|
||||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <UDPSocket.h>
|
||||
|
||||
#include "AudioRingBuffer.h"
|
||||
|
||||
const int MAX_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
|
||||
|
||||
class AudioInjector : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(uchar volume READ getVolume WRITE setVolume);
|
||||
public:
|
||||
AudioInjector(int maxNumSamples);
|
||||
~AudioInjector();
|
||||
|
||||
void injectAudio(UDPSocket* injectorSocket, sockaddr* destinationSocket);
|
||||
|
||||
bool isInjectingAudio() const { return _isInjectingAudio; }
|
||||
|
||||
unsigned char getVolume() const { return _volume; }
|
||||
void setVolume(unsigned char volume) { _volume = volume; }
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
void setPosition(const glm::vec3& position) { _position = position; }
|
||||
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
||||
|
||||
float getRadius() const { return _radius; }
|
||||
void setRadius(float radius) { _radius = radius; }
|
||||
|
||||
bool hasSamplesToInject() const { return _indexOfNextSlot > 0; }
|
||||
|
||||
void addSample(const int16_t sample);
|
||||
void addSamples(int16_t* sampleBuffer, int numSamples);
|
||||
|
||||
void clear();
|
||||
public slots:
|
||||
int16_t& sampleAt(const int index);
|
||||
void insertSample(const int index, int sample);
|
||||
private:
|
||||
QUuid _streamIdentifier;
|
||||
int16_t* _audioSampleArray;
|
||||
int _numTotalSamples;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
float _radius;
|
||||
unsigned char _volume;
|
||||
int _indexOfNextSlot;
|
||||
bool _isInjectingAudio;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AudioInjector__) */
|
|
@ -24,6 +24,8 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier)
|
|||
|
||||
}
|
||||
|
||||
const uchar MAX_INJECTOR_VOLUME = 255;
|
||||
|
||||
int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer);
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "AudioInjector.h"
|
||||
|
||||
#include "PositionalAudioRingBuffer.h"
|
||||
|
||||
class InjectedAudioRingBuffer : public PositionalAudioRingBuffer {
|
||||
|
|
|
@ -41,8 +41,10 @@ _velocity(0, 0, 0),
|
|||
_controllerButtons(0),
|
||||
_isActive(false),
|
||||
_leapID(LEAPID_INVALID),
|
||||
_sixenseID(SIXENSEID_INVALID),
|
||||
_numFramesWithoutData(0),
|
||||
_owningHandData(owningHandData)
|
||||
_owningHandData(owningHandData),
|
||||
_isCollidingWithVoxel(false)
|
||||
{
|
||||
for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) {
|
||||
_fingers.push_back(FingerData(this, owningHandData));
|
||||
|
|
|
@ -24,6 +24,7 @@ const int NUM_FINGERS_PER_HAND = 5;
|
|||
const int NUM_FINGERS = NUM_HANDS * NUM_FINGERS_PER_HAND;
|
||||
|
||||
const int LEAPID_INVALID = -1;
|
||||
const int SIXENSEID_INVALID = -1;
|
||||
|
||||
enum RaveGloveEffectsMode
|
||||
{
|
||||
|
@ -119,7 +120,7 @@ public:
|
|||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
||||
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
|
||||
int getFramesWithoutData() const { return _numFramesWithoutData; }
|
||||
|
||||
|
||||
private:
|
||||
glm::vec3 _tipRawPosition;
|
||||
glm::vec3 _rootRawPosition;
|
||||
|
@ -142,12 +143,16 @@ public:
|
|||
const glm::vec3& getRawNormal() const { return _rawNormal; }
|
||||
bool isActive() const { return _isActive; }
|
||||
int getLeapID() const { return _leapID; }
|
||||
int getSixenseID() const { return _sixenseID; }
|
||||
|
||||
|
||||
std::vector<FingerData>& getFingers() { return _fingers; }
|
||||
size_t getNumFingers() { return _fingers.size(); }
|
||||
|
||||
void setActive(bool active) { _isActive = active; }
|
||||
void setLeapID(int id) { _leapID = id; }
|
||||
void setSixenseID(int id) { _sixenseID = id; }
|
||||
|
||||
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
|
||||
void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; }
|
||||
void setVelocity(const glm::vec3& velocity) { _velocity = velocity; }
|
||||
|
@ -166,6 +171,9 @@ public:
|
|||
void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; }
|
||||
float getJoystickX() { return _joystickX; }
|
||||
float getJoystickY() { return _joystickY; }
|
||||
|
||||
bool getIsCollidingWithVoxel() { return _isCollidingWithVoxel; }
|
||||
void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; }
|
||||
|
||||
private:
|
||||
std::vector<FingerData> _fingers;
|
||||
|
@ -178,8 +186,12 @@ private:
|
|||
|
||||
bool _isActive; // This has current valid data
|
||||
int _leapID; // the Leap's serial id for this tracked object
|
||||
int _sixenseID; // Sixense controller ID for this palm
|
||||
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
|
||||
HandData* _owningHandData;
|
||||
|
||||
bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel
|
||||
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__HandData__) */
|
||||
|
|
|
@ -48,7 +48,7 @@ bool JurisdictionListener::queueJurisdictionRequest() {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (nodeList->getNodeActiveSocketOrPing(&(*node)) && node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
sockaddr* nodeAddress = node->getActiveSocket();
|
||||
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
||||
PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
|
||||
nodeCount++;
|
||||
}
|
||||
|
@ -64,9 +64,9 @@ bool JurisdictionListener::queueJurisdictionRequest() {
|
|||
return isStillRunning();
|
||||
}
|
||||
|
||||
void JurisdictionListener::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress);
|
||||
if (node) {
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
JurisdictionMap map;
|
||||
|
|
|
@ -45,7 +45,7 @@ protected:
|
|||
/// \param packetData pointer to received data
|
||||
/// \param ssize_t packetLength size of received data
|
||||
/// \thread "this" individual processing thread
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
private:
|
||||
NodeToJurisdictionMap _jurisdictions;
|
||||
|
|
|
@ -29,9 +29,9 @@ JurisdictionSender::~JurisdictionSender() {
|
|||
}
|
||||
|
||||
|
||||
void JurisdictionSender::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
void JurisdictionSender::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress);
|
||||
if (node) {
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
lockRequestingNodes();
|
||||
|
@ -66,7 +66,7 @@ bool JurisdictionSender::process() {
|
|||
Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (node->getActiveSocket() != NULL) {
|
||||
sockaddr* nodeAddress = node->getActiveSocket();
|
||||
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
||||
queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
|
||||
nodeCount++;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
virtual bool process();
|
||||
|
||||
protected:
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
/// Locks all the resources of the thread.
|
||||
void lockRequestingNodes() { pthread_mutex_lock(&_requestingNodeMutex); }
|
||||
|
|
|
@ -31,9 +31,6 @@ public:
|
|||
int count() const { return _elementsInUse; }
|
||||
|
||||
void deleteAll();
|
||||
|
||||
//static void voxelNodeDeleteHook(OctreeElement* element, void* extraData);
|
||||
|
||||
virtual void elementDeleted(OctreeElement* element);
|
||||
|
||||
private:
|
||||
|
|
|
@ -211,10 +211,6 @@ int Assignment::packToBuffer(unsigned char* buffer) {
|
|||
return numPackedBytes;
|
||||
}
|
||||
|
||||
void Assignment::run() {
|
||||
// run method ovveridden by subclasses
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const Assignment &assignment) {
|
||||
debug.nospace() << "UUID: " << assignment.getUUID().toString().toStdString().c_str() <<
|
||||
", Type: " << assignment.getType();
|
||||
|
|
|
@ -89,9 +89,6 @@ public:
|
|||
// implement parseData to return 0 so we can be a subclass of NodeData
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes) { return 0; }
|
||||
|
||||
/// blocking run of the assignment
|
||||
virtual void run();
|
||||
|
||||
friend QDebug operator<<(QDebug debug, const Assignment& assignment);
|
||||
friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
|
||||
friend QDataStream& operator>>(QDataStream &in, Assignment& assignment);
|
||||
|
|
113
libraries/shared/src/HifiSockAddr.cpp
Normal file
113
libraries/shared/src/HifiSockAddr.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// HifiSockAddr.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 11/26/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
#include <QtNetwork/QHostInfo>
|
||||
#include <QtNetwork/QNetworkInterface>
|
||||
|
||||
HifiSockAddr::HifiSockAddr() :
|
||||
_address(),
|
||||
_port(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HifiSockAddr::HifiSockAddr(const QHostAddress& address, quint16 port) :
|
||||
_address(address),
|
||||
_port(port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) {
|
||||
_address = otherSockAddr._address;
|
||||
_port = otherSockAddr._port;
|
||||
}
|
||||
|
||||
HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) {
|
||||
// lookup the IP by the hostname
|
||||
QHostInfo hostInfo = QHostInfo::fromName(hostname);
|
||||
foreach(const QHostAddress& address, hostInfo.addresses()) {
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_address = address;
|
||||
_port = hostOrderPort;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiSockAddr& HifiSockAddr::operator=(const HifiSockAddr& rhsSockAddr) {
|
||||
HifiSockAddr temp(rhsSockAddr);
|
||||
swap(temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void HifiSockAddr::swap(HifiSockAddr& otherSockAddr) {
|
||||
using std::swap;
|
||||
|
||||
swap(_address, otherSockAddr._address);
|
||||
swap(_port, otherSockAddr._port);
|
||||
}
|
||||
|
||||
int HifiSockAddr::packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr) {
|
||||
quint32 addressToPack = packSockAddr._address.isNull() ? 0 : packSockAddr._address.toIPv4Address();
|
||||
memcpy(packetData, &addressToPack, sizeof(addressToPack));
|
||||
memcpy(packetData + sizeof(addressToPack), &packSockAddr._port, sizeof(packSockAddr._port));
|
||||
|
||||
return sizeof(addressToPack) + sizeof(packSockAddr._port);
|
||||
}
|
||||
|
||||
int HifiSockAddr::unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr) {
|
||||
quint32* address = (quint32*) packetData;
|
||||
unpackDestSockAddr._address = *address == 0 ? QHostAddress() : QHostAddress(*address);
|
||||
unpackDestSockAddr._port = *((quint16*) (packetData + sizeof(quint32)));
|
||||
|
||||
return sizeof(quint32) + sizeof(quint16);
|
||||
}
|
||||
|
||||
bool HifiSockAddr::operator==(const HifiSockAddr &rhsSockAddr) const {
|
||||
return _address == rhsSockAddr._address && _port == rhsSockAddr._port;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const HifiSockAddr &hifiSockAddr) {
|
||||
debug.nospace() << hifiSockAddr._address.toString().toLocal8Bit().constData() << ":" << hifiSockAddr._port;
|
||||
return debug;
|
||||
}
|
||||
|
||||
quint32 getHostOrderLocalAddress() {
|
||||
|
||||
static int localAddress = 0;
|
||||
|
||||
if (localAddress == 0) {
|
||||
foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
|
||||
if (interface.flags() & QNetworkInterface::IsUp
|
||||
&& interface.flags() & QNetworkInterface::IsRunning
|
||||
&& interface.flags() & ~QNetworkInterface::IsLoopBack) {
|
||||
// we've decided that this is the active NIC
|
||||
// enumerate it's addresses to grab the IPv4 address
|
||||
foreach(const QNetworkAddressEntry &entry, interface.addressEntries()) {
|
||||
// make sure it's an IPv4 address that isn't the loopback
|
||||
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) {
|
||||
qDebug("Node's local address is %s\n", entry.ip().toString().toLocal8Bit().constData());
|
||||
|
||||
// set our localAddress and break out
|
||||
localAddress = entry.ip().toIPv4Address();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localAddress != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return the looked up local address
|
||||
return localAddress;
|
||||
}
|
50
libraries/shared/src/HifiSockAddr.h
Normal file
50
libraries/shared/src/HifiSockAddr.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// HifiSockAddr.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 11/26/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__HifiSockAddr__
|
||||
#define __hifi__HifiSockAddr__
|
||||
|
||||
#include <QtNetwork/QHostAddress>
|
||||
|
||||
class HifiSockAddr {
|
||||
public:
|
||||
HifiSockAddr();
|
||||
HifiSockAddr(const QHostAddress& address, quint16 port);
|
||||
HifiSockAddr(const HifiSockAddr& otherSockAddr);
|
||||
HifiSockAddr(const QString& hostname, quint16 hostOrderPort);
|
||||
|
||||
bool isNull() const { return _address.isNull() && _port == 0; }
|
||||
|
||||
HifiSockAddr& operator=(const HifiSockAddr& rhsSockAddr);
|
||||
void swap(HifiSockAddr& otherSockAddr);
|
||||
|
||||
bool operator==(const HifiSockAddr& rhsSockAddr) const;
|
||||
bool operator!=(const HifiSockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); }
|
||||
|
||||
const QHostAddress& getAddress() const { return _address; }
|
||||
QHostAddress* getAddressPointer() { return &_address; }
|
||||
void setAddress(const QHostAddress& address) { _address = address; }
|
||||
|
||||
quint16 getPort() const { return _port; }
|
||||
quint16* getPortPointer() { return &_port; }
|
||||
void setPort(quint16 port) { _port = port; }
|
||||
|
||||
static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr);
|
||||
static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr);
|
||||
|
||||
friend QDebug operator<<(QDebug debug, const HifiSockAddr &hifiSockAddr);
|
||||
private:
|
||||
QHostAddress _address;
|
||||
quint16 _port;
|
||||
};
|
||||
|
||||
quint32 getHostOrderLocalAddress();
|
||||
|
||||
Q_DECLARE_METATYPE(HifiSockAddr)
|
||||
|
||||
#endif /* defined(__hifi__HifiSockAddr__) */
|
|
@ -11,35 +11,35 @@
|
|||
#include <iostream>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "NodeList.h"
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
sockaddr_in Logging::logstashSocket = {};
|
||||
HifiSockAddr Logging::logstashSocket = HifiSockAddr();
|
||||
char* Logging::targetName = NULL;
|
||||
|
||||
sockaddr* Logging::socket() {
|
||||
const HifiSockAddr& Logging::socket() {
|
||||
|
||||
if (logstashSocket.sin_addr.s_addr == 0) {
|
||||
if (logstashSocket.getAddress().isNull()) {
|
||||
// we need to construct the socket object
|
||||
|
||||
// assume IPv4
|
||||
logstashSocket.sin_family = AF_INET;
|
||||
|
||||
// use the constant port
|
||||
logstashSocket.sin_port = htons(LOGSTASH_UDP_PORT);
|
||||
logstashSocket.setPort(htons(LOGSTASH_UDP_PORT));
|
||||
|
||||
// lookup the IP address for the constant hostname
|
||||
struct hostent* logstashHostInfo;
|
||||
if ((logstashHostInfo = gethostbyname(LOGSTASH_HOSTNAME))) {
|
||||
memcpy(&logstashSocket.sin_addr, logstashHostInfo->h_addr_list[0], logstashHostInfo->h_length);
|
||||
QHostInfo hostInfo = QHostInfo::fromName(LOGSTASH_HOSTNAME);
|
||||
if (!hostInfo.addresses().isEmpty()) {
|
||||
// use the first IP address
|
||||
logstashSocket.setAddress(hostInfo.addresses().first());
|
||||
} else {
|
||||
printf("Failed to lookup logstash IP - will try again on next log attempt.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return (sockaddr*) &logstashSocket;
|
||||
return logstashSocket;
|
||||
}
|
||||
|
||||
bool Logging::shouldSendStats() {
|
||||
|
@ -57,7 +57,8 @@ void Logging::stashValue(char statType, const char* key, float value) {
|
|||
NodeList *nodeList = NodeList::getInstance();
|
||||
|
||||
if (nodeList) {
|
||||
nodeList->getNodeSocket()->send(socket(), logstashPacket, numPacketBytes);
|
||||
nodeList->getNodeSocket().writeDatagram(logstashPacket, numPacketBytes,
|
||||
logstashSocket.getAddress(), logstashSocket.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,11 +20,13 @@ const char STAT_TYPE_TIMER = 't';
|
|||
const char STAT_TYPE_COUNTER = 'c';
|
||||
const char STAT_TYPE_GAUGE = 'g';
|
||||
|
||||
class HifiSockAddr;
|
||||
|
||||
/// Handles custom message handling and sending of stats/logs to Logstash instance
|
||||
class Logging {
|
||||
public:
|
||||
/// \return the socket used to send stats to logstash
|
||||
static sockaddr* socket();
|
||||
static const HifiSockAddr& socket();
|
||||
|
||||
/// checks if this target should send stats to logstash, given its current environment
|
||||
/// \return true if the caller should send stats to logstash
|
||||
|
@ -44,7 +46,7 @@ public:
|
|||
/// prints various process, message type, and time information
|
||||
static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message);
|
||||
private:
|
||||
static sockaddr_in logstashSocket;
|
||||
static HifiSockAddr logstashSocket;
|
||||
static char* targetName;
|
||||
};
|
||||
|
||||
|
|
|
@ -22,10 +22,10 @@ NetworkPacket::~NetworkPacket() {
|
|||
// nothing to do
|
||||
}
|
||||
|
||||
void NetworkPacket::copyContents(const sockaddr& address, const unsigned char* packetData, ssize_t packetLength) {
|
||||
void NetworkPacket::copyContents(const HifiSockAddr& sockAddr, const unsigned char* packetData, ssize_t packetLength) {
|
||||
_packetLength = 0;
|
||||
if (packetLength >=0 && packetLength <= MAX_PACKET_SIZE) {
|
||||
memcpy(&_address, &address, sizeof(_address));
|
||||
_sockAddr = sockAddr;
|
||||
_packetLength = packetLength;
|
||||
memcpy(&_packetData[0], packetData, packetLength);
|
||||
} else {
|
||||
|
@ -34,16 +34,16 @@ void NetworkPacket::copyContents(const sockaddr& address, const unsigned char*
|
|||
}
|
||||
|
||||
NetworkPacket::NetworkPacket(const NetworkPacket& packet) {
|
||||
copyContents(packet.getAddress(), packet.getData(), packet.getLength());
|
||||
copyContents(packet.getSockAddr(), packet.getData(), packet.getLength());
|
||||
}
|
||||
|
||||
NetworkPacket::NetworkPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
copyContents(address, packetData, packetLength);
|
||||
NetworkPacket::NetworkPacket(const HifiSockAddr& sockAddr, unsigned char* packetData, ssize_t packetLength) {
|
||||
copyContents(sockAddr, packetData, packetLength);
|
||||
};
|
||||
|
||||
// copy assignment
|
||||
NetworkPacket& NetworkPacket::operator=(NetworkPacket const& other) {
|
||||
copyContents(other.getAddress(), other.getData(), other.getLength());
|
||||
copyContents(other.getSockAddr(), other.getData(), other.getLength());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
#include "NodeList.h" // for MAX_PACKET_SIZE
|
||||
|
||||
/// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet
|
||||
|
@ -30,19 +32,19 @@ public:
|
|||
NetworkPacket& operator= (NetworkPacket&& other); // move assignment
|
||||
#endif
|
||||
|
||||
NetworkPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength);
|
||||
NetworkPacket(const HifiSockAddr& sockAddr, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
sockaddr& getAddress() { return _address; }
|
||||
const HifiSockAddr& getSockAddr() const { return _sockAddr; }
|
||||
ssize_t getLength() const { return _packetLength; }
|
||||
unsigned char* getData() { return &_packetData[0]; }
|
||||
|
||||
const sockaddr& getAddress() const { return _address; }
|
||||
const HifiSockAddr& getAddress() const { return _sockAddr; }
|
||||
const unsigned char* getData() const { return &_packetData[0]; }
|
||||
|
||||
private:
|
||||
void copyContents(const sockaddr& address, const unsigned char* packetData, ssize_t packetLength);
|
||||
void copyContents(const HifiSockAddr& sockAddr, const unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
sockaddr _address;
|
||||
HifiSockAddr _sockAddr;
|
||||
ssize_t _packetLength;
|
||||
unsigned char _packetData[MAX_PACKET_SIZE];
|
||||
};
|
||||
|
|
|
@ -19,30 +19,25 @@
|
|||
#include "Node.h"
|
||||
#include "NodeTypes.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "UDPSocket.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
Node::Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket) :
|
||||
Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) :
|
||||
_type(type),
|
||||
_uuid(uuid),
|
||||
_wakeMicrostamp(usecTimestampNow()),
|
||||
_lastHeardMicrostamp(usecTimestampNow()),
|
||||
_publicSocket(publicSocket),
|
||||
_localSocket(localSocket),
|
||||
_activeSocket(NULL),
|
||||
_bytesReceivedMovingAverage(NULL),
|
||||
_linkedData(NULL),
|
||||
_isAlive(true)
|
||||
{
|
||||
setPublicSocket(publicSocket);
|
||||
setLocalSocket(localSocket);
|
||||
|
||||
pthread_mutex_init(&_mutex, 0);
|
||||
}
|
||||
|
||||
Node::~Node() {
|
||||
delete _publicSocket;
|
||||
delete _localSocket;
|
||||
|
||||
if (_linkedData) {
|
||||
_linkedData->deleteOrDeleteLater();
|
||||
}
|
||||
|
@ -86,47 +81,32 @@ const char* Node::getTypeName() const {
|
|||
}
|
||||
}
|
||||
|
||||
void Node::setPublicSocket(sockaddr* publicSocket) {
|
||||
if (_activeSocket == _publicSocket) {
|
||||
void Node::setPublicSocket(const HifiSockAddr& publicSocket) {
|
||||
if (_activeSocket == &_publicSocket) {
|
||||
// if the active socket was the public socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (publicSocket) {
|
||||
_publicSocket = new sockaddr(*publicSocket);
|
||||
} else {
|
||||
_publicSocket = NULL;
|
||||
}
|
||||
_publicSocket = publicSocket;
|
||||
}
|
||||
|
||||
void Node::setLocalSocket(sockaddr* localSocket) {
|
||||
if (_activeSocket == _localSocket) {
|
||||
void Node::setLocalSocket(const HifiSockAddr& localSocket) {
|
||||
if (_activeSocket == &_localSocket) {
|
||||
// if the active socket was the local socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (localSocket) {
|
||||
_localSocket = new sockaddr(*localSocket);
|
||||
} else {
|
||||
_localSocket = NULL;
|
||||
}
|
||||
_localSocket = localSocket;
|
||||
}
|
||||
|
||||
void Node::activateLocalSocket() {
|
||||
qDebug() << "Activating local socket for node" << *this << "\n";
|
||||
_activeSocket = _localSocket;
|
||||
_activeSocket = &_localSocket;
|
||||
}
|
||||
|
||||
void Node::activatePublicSocket() {
|
||||
qDebug() << "Activating public socket for node" << *this << "\n";
|
||||
_activeSocket = _publicSocket;
|
||||
}
|
||||
|
||||
bool Node::matches(sockaddr* otherPublicSocket, sockaddr* otherLocalSocket, char otherNodeType) {
|
||||
// checks if two node objects are the same node (same type + local + public address)
|
||||
return _type == otherNodeType
|
||||
&& socketMatch(_publicSocket, otherPublicSocket)
|
||||
&& socketMatch(_localSocket, otherLocalSocket);
|
||||
_activeSocket = &_publicSocket;
|
||||
}
|
||||
|
||||
void Node::recordBytesReceived(int bytesReceived) {
|
||||
|
@ -154,15 +134,8 @@ float Node::getAverageKilobitsPerSecond() {
|
|||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const Node &node) {
|
||||
char publicAddressBuffer[16] = {'\0'};
|
||||
unsigned short publicAddressPort = loadBufferWithSocketInfo(publicAddressBuffer, node.getPublicSocket());
|
||||
|
||||
char localAddressBuffer[16] = {'\0'};
|
||||
unsigned short localAddressPort = loadBufferWithSocketInfo(localAddressBuffer, node.getLocalSocket());
|
||||
|
||||
debug.nospace() << node.getTypeName() << " (" << node.getType() << ")";
|
||||
debug << " " << node.getUUID().toString().toLocal8Bit().constData() << " ";
|
||||
debug.nospace() << publicAddressBuffer << ":" << publicAddressPort;
|
||||
debug.nospace() << " / " << localAddressBuffer << ":" << localAddressPort;
|
||||
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
|
||||
return debug.nospace();
|
||||
}
|
||||
|
|
|
@ -21,19 +21,18 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
#include "NodeData.h"
|
||||
#include "SimpleMovingAverage.h"
|
||||
|
||||
class Node {
|
||||
public:
|
||||
Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket);
|
||||
Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
~Node();
|
||||
|
||||
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
|
||||
bool operator!=(const Node& otherNode) const { return !(*this == otherNode); }
|
||||
|
||||
bool matches(sockaddr* otherPublicSocket, sockaddr* otherLocalSocket, char otherNodeType);
|
||||
|
||||
char getType() const { return _type; }
|
||||
void setType(char type) { _type = type; }
|
||||
const char* getTypeName() const;
|
||||
|
@ -47,12 +46,12 @@ public:
|
|||
uint64_t getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
|
||||
void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||
|
||||
sockaddr* getPublicSocket() const { return _publicSocket; }
|
||||
void setPublicSocket(sockaddr* publicSocket);
|
||||
sockaddr* getLocalSocket() const { return _localSocket; }
|
||||
void setLocalSocket(sockaddr* localSocket);
|
||||
const HifiSockAddr& getPublicSocket() const { return _publicSocket; }
|
||||
void setPublicSocket(const HifiSockAddr& publicSocket);
|
||||
const HifiSockAddr& getLocalSocket() const { return _localSocket; }
|
||||
void setLocalSocket(const HifiSockAddr& localSocket);
|
||||
|
||||
sockaddr* getActiveSocket() const { return _activeSocket; }
|
||||
const HifiSockAddr* getActiveSocket() const { return _activeSocket; }
|
||||
|
||||
void activatePublicSocket();
|
||||
void activateLocalSocket();
|
||||
|
@ -87,9 +86,9 @@ private:
|
|||
QUuid _uuid;
|
||||
uint64_t _wakeMicrostamp;
|
||||
uint64_t _lastHeardMicrostamp;
|
||||
sockaddr* _publicSocket;
|
||||
sockaddr* _localSocket;
|
||||
sockaddr* _activeSocket;
|
||||
HifiSockAddr _publicSocket;
|
||||
HifiSockAddr _localSocket;
|
||||
HifiSockAddr* _activeSocket;
|
||||
SimpleMovingAverage* _bytesReceivedMovingAverage;
|
||||
NodeData* _linkedData;
|
||||
bool _isAlive;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
class Node;
|
||||
|
||||
class NodeData : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeData(Node* owningNode = NULL);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "Assignment.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "Logging.h"
|
||||
#include "NodeList.h"
|
||||
#include "NodeTypes.h"
|
||||
|
@ -61,22 +62,21 @@ NodeList* NodeList::getInstance() {
|
|||
|
||||
NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
||||
_domainHostname(DEFAULT_DOMAIN_HOSTNAME),
|
||||
_domainIP(),
|
||||
_domainPort(DEFAULT_DOMAIN_SERVER_PORT),
|
||||
_domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_nodeBuckets(),
|
||||
_numNodes(0),
|
||||
_nodeSocket(newSocketListenPort),
|
||||
_nodeSocket(),
|
||||
_ownerType(newOwnerType),
|
||||
_nodeTypesOfInterest(NULL),
|
||||
_ownerUUID(QUuid::createUuid()),
|
||||
_numNoReplyDomainCheckIns(0),
|
||||
_assignmentServerSocket(NULL),
|
||||
_publicAddress(),
|
||||
_publicPort(0),
|
||||
_assignmentServerSocket(),
|
||||
_publicSockAddr(),
|
||||
_hasCompletedInitialSTUNFailure(false),
|
||||
_stunRequestsSinceSuccess(0)
|
||||
{
|
||||
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, newSocketListenPort);
|
||||
qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort() << "\n";
|
||||
}
|
||||
|
||||
NodeList::~NodeList() {
|
||||
|
@ -100,29 +100,29 @@ void NodeList::setDomainHostname(const QString& domainHostname) {
|
|||
_domainHostname = domainHostname.left(colonIndex);
|
||||
|
||||
// grab the port by reading the string after the colon
|
||||
_domainPort = atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData());
|
||||
_domainSockAddr.setPort(atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData()));
|
||||
|
||||
qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainPort << "\n";
|
||||
qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainSockAddr.getPort() << "\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;
|
||||
_domainSockAddr.setPort(DEFAULT_DOMAIN_SERVER_PORT);
|
||||
}
|
||||
|
||||
// clear the NodeList so nodes from this domain are killed
|
||||
clear();
|
||||
|
||||
// reset our _domainIP to the null address so that a lookup happens on next check in
|
||||
_domainIP.clear();
|
||||
_domainSockAddr.setAddress(QHostAddress::Null);
|
||||
notifyDomainChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
|
||||
void NodeList::timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData) {
|
||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
if (socketMatch(node->getPublicSocket(), nodeAddress) ||
|
||||
socketMatch(node->getLocalSocket(), nodeAddress)) {
|
||||
if (node->getPublicSocket() == nodeAddress ||
|
||||
node->getLocalSocket() == nodeAddress) {
|
||||
|
||||
int pingTime = usecTimestampNow() - *(uint64_t*)(packetData + numBytesForPacketHeader(packetData));
|
||||
|
||||
|
@ -132,11 +132,11 @@ void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
|
|||
}
|
||||
}
|
||||
|
||||
void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetData, size_t dataBytes) {
|
||||
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, unsigned char* packetData, size_t dataBytes) {
|
||||
switch (packetData[0]) {
|
||||
case PACKET_TYPE_DOMAIN: {
|
||||
// only process the DS if this is our current domain server
|
||||
if (_domainIP == QHostAddress(senderAddress)) {
|
||||
if (_domainSockAddr == senderSockAddr) {
|
||||
processDomainServerList(packetData, dataBytes);
|
||||
}
|
||||
|
||||
|
@ -145,15 +145,15 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat
|
|||
case PACKET_TYPE_PING: {
|
||||
// send it right back
|
||||
populateTypeAndVersion(packetData, PACKET_TYPE_PING_REPLY);
|
||||
_nodeSocket.send(senderAddress, packetData, dataBytes);
|
||||
_nodeSocket.writeDatagram((char*) packetData, dataBytes, senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_PING_REPLY: {
|
||||
// activate the appropriate socket for this node, if not yet updated
|
||||
activateSocketFromNodeCommunication(senderAddress);
|
||||
activateSocketFromNodeCommunication(senderSockAddr);
|
||||
|
||||
// set the ping time for this node for stat collection
|
||||
timePingReply(senderAddress, packetData);
|
||||
timePingReply(senderSockAddr, packetData);
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_STUN_RESPONSE: {
|
||||
|
@ -169,7 +169,7 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat
|
|||
}
|
||||
}
|
||||
|
||||
void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes) {
|
||||
void NodeList::processBulkNodeData(const HifiSockAddr& senderAddress, unsigned char *packetData, int numTotalBytes) {
|
||||
|
||||
// find the avatar mixer in our node list and update the lastRecvTime from it
|
||||
Node* bulkSendNode = nodeWithAddress(senderAddress);
|
||||
|
@ -198,11 +198,11 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
|||
|
||||
if (!matchingNode) {
|
||||
// we're missing this node, we need to add it to the list
|
||||
matchingNode = addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, NULL, NULL);
|
||||
matchingNode = addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, HifiSockAddr(), HifiSockAddr());
|
||||
}
|
||||
|
||||
currentPosition += updateNodeWithData(matchingNode,
|
||||
NULL,
|
||||
HifiSockAddr(),
|
||||
packetHolder,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
|
||||
|
@ -210,16 +210,16 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
|||
}
|
||||
}
|
||||
|
||||
int NodeList::updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes) {
|
||||
int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes) {
|
||||
node->lock();
|
||||
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
if (senderAddress) {
|
||||
activateSocketFromNodeCommunication(senderAddress);
|
||||
if (!senderSockAddr.isNull()) {
|
||||
activateSocketFromNodeCommunication(senderSockAddr);
|
||||
}
|
||||
|
||||
if (node->getActiveSocket() || !senderAddress) {
|
||||
if (node->getActiveSocket() || senderSockAddr.isNull()) {
|
||||
node->recordBytesReceived(dataBytes);
|
||||
|
||||
if (!node->getLinkedData() && linkedDataCreateCallback) {
|
||||
|
@ -238,9 +238,9 @@ int NodeList::updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned c
|
|||
}
|
||||
}
|
||||
|
||||
Node* NodeList::nodeWithAddress(sockaddr *senderAddress) {
|
||||
Node* NodeList::nodeWithAddress(const HifiSockAddr &senderSockAddr) {
|
||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
if (node->getActiveSocket() && socketMatch(node->getActiveSocket(), senderAddress)) {
|
||||
if (node->getActiveSocket() && *node->getActiveSocket() == senderSockAddr) {
|
||||
return &(*node);
|
||||
}
|
||||
}
|
||||
|
@ -340,25 +340,15 @@ void NodeList::sendSTUNRequest() {
|
|||
memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID));
|
||||
|
||||
// lookup the IP for the STUN server
|
||||
static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME);
|
||||
static HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
|
||||
for (int i = 0; i < stunInfo.addresses().size(); i++) {
|
||||
if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
QString stunIPAddress = stunInfo.addresses()[i].toString();
|
||||
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
qDebug("Sending intial stun request to %s\n", stunIPAddress.toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
_nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
|
||||
STUN_SERVER_PORT,
|
||||
stunRequestPacket,
|
||||
sizeof(stunRequestPacket));
|
||||
|
||||
break;
|
||||
}
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
qDebug("Sending intial stun request to %s\n", stunSockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
|
||||
stunSockAddr.getAddress(), stunSockAddr.getPort());
|
||||
|
||||
_stunRequestsSinceSuccess++;
|
||||
|
||||
if (_stunRequestsSinceSuccess >= NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
|
||||
|
@ -372,8 +362,8 @@ void NodeList::sendSTUNRequest() {
|
|||
}
|
||||
|
||||
// reset the public address and port
|
||||
_publicAddress = QHostAddress::Null;
|
||||
_publicPort = 0;
|
||||
// use 0 so the DS knows to act as out STUN server
|
||||
_publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -425,13 +415,12 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
|
|||
|
||||
QHostAddress newPublicAddress = QHostAddress(stunAddress);
|
||||
|
||||
if (newPublicAddress != _publicAddress || newPublicPort != _publicPort) {
|
||||
_publicAddress = newPublicAddress;
|
||||
_publicPort = newPublicPort;
|
||||
if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) {
|
||||
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
|
||||
|
||||
qDebug("New public socket received from STUN server is %s:%hu\n",
|
||||
_publicAddress.toString().toLocal8Bit().constData(),
|
||||
_publicPort);
|
||||
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
|
||||
_publicSockAddr.getPort());
|
||||
|
||||
}
|
||||
|
||||
|
@ -489,17 +478,17 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
static bool printedDomainServerIP = false;
|
||||
|
||||
// Lookup the IP address of the domain server if we need to
|
||||
if (_domainIP.isNull()) {
|
||||
if (_domainSockAddr.getAddress().isNull()) {
|
||||
qDebug("Looking up DS hostname %s.\n", _domainHostname.toLocal8Bit().constData());
|
||||
|
||||
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];
|
||||
_domainSockAddr.setAddress(domainServerHostInfo.addresses()[i]);
|
||||
|
||||
qDebug("DS at %s is at %s\n", _domainHostname.toLocal8Bit().constData(),
|
||||
_domainIP.toString().toLocal8Bit().constData());
|
||||
_domainSockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
|
||||
printedDomainServerIP = true;
|
||||
|
||||
|
@ -512,11 +501,11 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
}
|
||||
}
|
||||
} else if (!printedDomainServerIP) {
|
||||
qDebug("Domain Server IP: %s\n", _domainIP.toString().toLocal8Bit().constData());
|
||||
qDebug("Domain Server IP: %s\n", _domainSockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
printedDomainServerIP = true;
|
||||
}
|
||||
|
||||
if (_publicAddress.isNull() && !_hasCompletedInitialSTUNFailure) {
|
||||
if (_publicSockAddr.isNull() && !_hasCompletedInitialSTUNFailure) {
|
||||
// we don't know our public socket and we need to send it to the domain server
|
||||
// send a STUN request to figure it out
|
||||
sendSTUNRequest();
|
||||
|
@ -548,13 +537,12 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
packetPosition += rfcOwnerUUID.size();
|
||||
|
||||
// pack our public address to send to domain-server
|
||||
packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket),
|
||||
htonl(_publicAddress.toIPv4Address()), htons(_publicPort));
|
||||
packetPosition += HifiSockAddr::packSockAddr(checkInPacket + (packetPosition - checkInPacket), _publicSockAddr);
|
||||
|
||||
// pack our local address to send to domain-server
|
||||
packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket),
|
||||
getLocalAddress(),
|
||||
htons(_nodeSocket.getListeningPort()));
|
||||
packetPosition += HifiSockAddr::packSockAddr(checkInPacket + (packetPosition - checkInPacket),
|
||||
HifiSockAddr(QHostAddress(getHostOrderLocalAddress()),
|
||||
_nodeSocket.localPort()));
|
||||
|
||||
// add the number of bytes for node types of interest
|
||||
*(packetPosition++) = numBytesNodesOfInterest;
|
||||
|
@ -567,9 +555,8 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
packetPosition += numBytesNodesOfInterest;
|
||||
}
|
||||
|
||||
_nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, checkInPacket,
|
||||
packetPosition - checkInPacket);
|
||||
|
||||
_nodeSocket.writeDatagram((char*) checkInPacket, packetPosition - checkInPacket,
|
||||
_domainSockAddr.getAddress(), _domainSockAddr.getPort());
|
||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||
static unsigned int numDomainCheckins = 0;
|
||||
|
||||
|
@ -592,10 +579,8 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte
|
|||
char nodeType;
|
||||
|
||||
// assumes only IPv4 addresses
|
||||
sockaddr_in nodePublicSocket;
|
||||
nodePublicSocket.sin_family = AF_INET;
|
||||
sockaddr_in nodeLocalSocket;
|
||||
nodeLocalSocket.sin_family = AF_INET;
|
||||
HifiSockAddr nodePublicSocket;
|
||||
HifiSockAddr nodeLocalSocket;
|
||||
|
||||
unsigned char* readPtr = packetData + numBytesForPacketHeader(packetData);
|
||||
unsigned char* startPtr = packetData;
|
||||
|
@ -605,24 +590,22 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte
|
|||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) readPtr, NUM_BYTES_RFC4122_UUID));
|
||||
readPtr += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
readPtr += unpackSocket(readPtr, (sockaddr*) &nodePublicSocket);
|
||||
readPtr += unpackSocket(readPtr, (sockaddr*) &nodeLocalSocket);
|
||||
readPtr += HifiSockAddr::unpackSockAddr(readPtr, nodePublicSocket);
|
||||
readPtr += HifiSockAddr::unpackSockAddr(readPtr, nodeLocalSocket);
|
||||
|
||||
// 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) {
|
||||
nodePublicSocket.sin_addr.s_addr = htonl(_domainIP.toIPv4Address());
|
||||
if (nodePublicSocket.getAddress().isNull()) {
|
||||
nodePublicSocket.setAddress(_domainSockAddr.getAddress());
|
||||
}
|
||||
|
||||
addOrUpdateNode(nodeUUID, nodeType, (sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket);
|
||||
addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket);
|
||||
}
|
||||
|
||||
|
||||
return readNodes;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
|
@ -633,14 +616,18 @@ void NodeList::sendAssignment(Assignment& assignment) {
|
|||
int numHeaderBytes = populateTypeAndVersion(assignmentPacket, assignmentPacketType);
|
||||
int numAssignmentBytes = assignment.packToBuffer(assignmentPacket + numHeaderBytes);
|
||||
|
||||
sockaddr* assignmentServerSocket = (_assignmentServerSocket == NULL)
|
||||
? (sockaddr*) &DEFAULT_LOCAL_ASSIGNMENT_SOCKET
|
||||
: _assignmentServerSocket;
|
||||
static HifiSockAddr DEFAULT_ASSIGNMENT_SOCKET(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, DEFAULT_DOMAIN_SERVER_PORT);
|
||||
|
||||
_nodeSocket.send(assignmentServerSocket, assignmentPacket, numHeaderBytes + numAssignmentBytes);
|
||||
const HifiSockAddr* assignmentServerSocket = _assignmentServerSocket.isNull()
|
||||
? &DEFAULT_ASSIGNMENT_SOCKET
|
||||
: &_assignmentServerSocket;
|
||||
|
||||
_nodeSocket.writeDatagram((char*) assignmentPacket, numHeaderBytes + numAssignmentBytes,
|
||||
assignmentServerSocket->getAddress(),
|
||||
assignmentServerSocket->getPort());
|
||||
}
|
||||
|
||||
void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) const {
|
||||
void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) {
|
||||
|
||||
uint64_t currentTime = 0;
|
||||
|
||||
|
@ -652,11 +639,14 @@ void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) const {
|
|||
memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime));
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
_nodeSocket.send(node->getLocalSocket(), pingPacket, sizeof(pingPacket));
|
||||
_nodeSocket.send(node->getPublicSocket(), pingPacket, sizeof(pingPacket));
|
||||
_nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket),
|
||||
node->getLocalSocket().getAddress(), node->getLocalSocket().getPort());
|
||||
_nodeSocket.writeDatagram((char*) pingPacket, sizeof(pingPacket),
|
||||
node->getPublicSocket().getAddress(), node->getPublicSocket().getPort());
|
||||
}
|
||||
|
||||
Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket) {
|
||||
Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
|
||||
NodeList::iterator node = end();
|
||||
|
||||
for (node = begin(); node != end(); node++) {
|
||||
|
@ -684,12 +674,12 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publ
|
|||
}
|
||||
|
||||
// check if we need to change this node's public or local sockets
|
||||
if (!socketMatch(publicSocket, node->getPublicSocket())) {
|
||||
if (publicSocket != node->getPublicSocket()) {
|
||||
node->setPublicSocket(publicSocket);
|
||||
qDebug() << "Public socket change for node" << *node << "\n";
|
||||
}
|
||||
|
||||
if (!socketMatch(localSocket, node->getLocalSocket())) {
|
||||
if (localSocket != node->getLocalSocket()) {
|
||||
node->setLocalSocket(localSocket);
|
||||
qDebug() << "Local socket change for node" << *node << "\n";
|
||||
}
|
||||
|
@ -725,7 +715,8 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt
|
|||
if (memchr(nodeTypes, node->getType(), numNodeTypes)) {
|
||||
if (getNodeActiveSocketOrPing(&(*node))) {
|
||||
// we know which socket is good for this node, send there
|
||||
_nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes);
|
||||
_nodeSocket.writeDatagram((char*) broadcastData, dataBytes,
|
||||
node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort());
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
@ -733,25 +724,16 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt
|
|||
return n;
|
||||
}
|
||||
|
||||
const uint64_t PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
|
||||
|
||||
void NodeList::possiblyPingInactiveNodes() {
|
||||
static timeval lastPing = {};
|
||||
|
||||
// make sure PING_INACTIVE_NODE_INTERVAL_USECS has elapsed since last ping
|
||||
if (usecTimestampNow() - usecTimestamp(&lastPing) >= PING_INACTIVE_NODE_INTERVAL_USECS) {
|
||||
gettimeofday(&lastPing, NULL);
|
||||
|
||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active link to this node, ping it to set that up
|
||||
pingPublicAndLocalSocketsForInactiveNode(&(*node));
|
||||
}
|
||||
void NodeList::pingInactiveNodes() {
|
||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active link to this node, ping it to set that up
|
||||
pingPublicAndLocalSocketsForInactiveNode(&(*node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sockaddr* NodeList::getNodeActiveSocketOrPing(Node* node) {
|
||||
const HifiSockAddr* NodeList::getNodeActiveSocketOrPing(Node* node) {
|
||||
if (node->getActiveSocket()) {
|
||||
return node->getActiveSocket();
|
||||
} else {
|
||||
|
@ -760,15 +742,15 @@ sockaddr* NodeList::getNodeActiveSocketOrPing(Node* node) {
|
|||
}
|
||||
}
|
||||
|
||||
void NodeList::activateSocketFromNodeCommunication(sockaddr *nodeAddress) {
|
||||
void NodeList::activateSocketFromNodeCommunication(const HifiSockAddr& nodeAddress) {
|
||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
if (!node->getActiveSocket()) {
|
||||
// check both the public and local addresses for each node to see if we find a match
|
||||
// prioritize the private address so that we prune erroneous local matches
|
||||
if (socketMatch(node->getPublicSocket(), nodeAddress)) {
|
||||
if (node->getPublicSocket() == nodeAddress) {
|
||||
node->activatePublicSocket();
|
||||
break;
|
||||
} else if (socketMatch(node->getLocalSocket(), nodeAddress)) {
|
||||
} else if (node->getLocalSocket() == nodeAddress) {
|
||||
node->activateLocalSocket();
|
||||
break;
|
||||
}
|
||||
|
@ -804,7 +786,22 @@ void NodeList::killNode(Node* node, bool mustLockNode) {
|
|||
}
|
||||
}
|
||||
|
||||
void* removeSilentNodes(void *args) {
|
||||
void NodeList::removeSilentNodes() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) {
|
||||
node->lock();
|
||||
|
||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
||||
// kill this node, don't lock - we already did it
|
||||
nodeList->killNode(&(*node), false);
|
||||
}
|
||||
|
||||
node->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void* removeSilentNodesAndSleep(void *args) {
|
||||
NodeList* nodeList = (NodeList*) args;
|
||||
uint64_t checkTimeUsecs = 0;
|
||||
int sleepTime = 0;
|
||||
|
@ -813,16 +810,7 @@ void* removeSilentNodes(void *args) {
|
|||
|
||||
checkTimeUsecs = usecTimestampNow();
|
||||
|
||||
for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) {
|
||||
node->lock();
|
||||
|
||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
||||
// kill this node, don't lock - we already did it
|
||||
nodeList->killNode(&(*node), false);
|
||||
}
|
||||
|
||||
node->unlock();
|
||||
}
|
||||
nodeList->removeSilentNodes();
|
||||
|
||||
sleepTime = NODE_SILENCE_THRESHOLD_USECS - (usecTimestampNow() - checkTimeUsecs);
|
||||
|
||||
|
@ -845,7 +833,7 @@ void* removeSilentNodes(void *args) {
|
|||
|
||||
void NodeList::startSilentNodeRemovalThread() {
|
||||
if (!::silentNodeThreadStopFlag) {
|
||||
pthread_create(&removeSilentNodesThread, NULL, removeSilentNodes, (void*) this);
|
||||
pthread_create(&removeSilentNodesThread, NULL, removeSilentNodesAndSleep, (void*) this);
|
||||
} else {
|
||||
qDebug("Refusing to start silent node removal thread from previously failed join.\n");
|
||||
}
|
||||
|
@ -969,7 +957,7 @@ void NodeListIterator::skipDeadAndStopIncrement() {
|
|||
|
||||
void NodeList::addDomainListener(DomainChangeListener* listener) {
|
||||
_domainListeners.push_back(listener);
|
||||
QString domain = _domainHostname.isEmpty() ? _domainIP.toString() : _domainHostname;
|
||||
QString domain = _domainHostname.isEmpty() ? _domainSockAddr.getAddress().toString() : _domainHostname;
|
||||
listener->domainChanged(domain);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include "Node.h"
|
||||
#include "NodeTypes.h"
|
||||
#include "UDPSocket.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "pthread.h"
|
||||
|
@ -31,7 +31,8 @@ const int NODES_PER_BUCKET = 100;
|
|||
const int MAX_PACKET_SIZE = 1500;
|
||||
|
||||
const uint64_t NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000;
|
||||
const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
|
||||
const uint64_t DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
|
||||
const uint64_t PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
|
||||
|
||||
extern const char SOLO_NODE_TYPES[2];
|
||||
|
||||
|
@ -40,11 +41,12 @@ const int MAX_HOSTNAME_BYTES = 256;
|
|||
extern const QString DEFAULT_DOMAIN_HOSTNAME;
|
||||
extern const unsigned short DEFAULT_DOMAIN_SERVER_PORT;
|
||||
|
||||
const char LOCAL_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost";
|
||||
const char DEFAULT_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost";
|
||||
|
||||
const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
|
||||
|
||||
class Assignment;
|
||||
class HifiSockAddr;
|
||||
class NodeListIterator;
|
||||
|
||||
// Callers who want to hook add/kill callbacks should implement this class
|
||||
|
@ -59,7 +61,8 @@ public:
|
|||
virtual void domainChanged(QString domain) = 0;
|
||||
};
|
||||
|
||||
class NodeList {
|
||||
class NodeList : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = 0);
|
||||
static NodeList* getInstance();
|
||||
|
@ -75,19 +78,17 @@ public:
|
|||
const QString& getDomainHostname() const { return _domainHostname; }
|
||||
void setDomainHostname(const QString& domainHostname);
|
||||
|
||||
const QHostAddress& getDomainIP() const { return _domainIP; }
|
||||
void setDomainIP(const QHostAddress& domainIP) { _domainIP = domainIP; }
|
||||
void setDomainIPToLocalhost() { _domainIP = QHostAddress(INADDR_LOOPBACK); }
|
||||
const QHostAddress& getDomainIP() const { return _domainSockAddr.getAddress(); }
|
||||
void setDomainIPToLocalhost() { _domainSockAddr.setAddress(QHostAddress(INADDR_LOOPBACK)); }
|
||||
|
||||
unsigned short getDomainPort() const { return _domainPort; }
|
||||
void setDomainPort(unsigned short domainPort) { _domainPort = domainPort; }
|
||||
void setDomainSockAddr(const HifiSockAddr& domainSockAddr) { _domainSockAddr = domainSockAddr; }
|
||||
|
||||
unsigned short getDomainPort() const { return _domainSockAddr.getPort(); }
|
||||
|
||||
const QUuid& getOwnerUUID() const { return _ownerUUID; }
|
||||
void setOwnerUUID(const QUuid& ownerUUID) { _ownerUUID = ownerUUID; }
|
||||
|
||||
UDPSocket* getNodeSocket() { return &_nodeSocket; }
|
||||
|
||||
unsigned short int getSocketListenPort() const { return _nodeSocket.getListeningPort(); }
|
||||
QUdpSocket& getNodeSocket() { return _nodeSocket; }
|
||||
|
||||
void(*linkedDataCreateCallback)(Node *);
|
||||
|
||||
|
@ -101,26 +102,25 @@ public:
|
|||
|
||||
void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest);
|
||||
|
||||
void sendDomainServerCheckIn();
|
||||
int processDomainServerList(unsigned char *packetData, size_t dataBytes);
|
||||
|
||||
void setAssignmentServerSocket(sockaddr* serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void sendAssignment(Assignment& assignment);
|
||||
|
||||
void pingPublicAndLocalSocketsForInactiveNode(Node* node) const;
|
||||
void pingPublicAndLocalSocketsForInactiveNode(Node* node);
|
||||
|
||||
void sendKillNode(const char* nodeTypes, int numNodeTypes);
|
||||
|
||||
Node* nodeWithAddress(sockaddr *senderAddress);
|
||||
Node* nodeWithAddress(const HifiSockAddr& senderSockAddr);
|
||||
Node* nodeWithUUID(const QUuid& nodeUUID);
|
||||
|
||||
Node* addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket);
|
||||
Node* addOrUpdateNode(const QUuid& uuid, char nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
void killNode(Node* node, bool mustLockNode = true);
|
||||
|
||||
void processNodeData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes);
|
||||
void processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes);
|
||||
void processNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, size_t dataBytes);
|
||||
void processBulkNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, int numTotalBytes);
|
||||
|
||||
int updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes);
|
||||
int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes);
|
||||
|
||||
unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes);
|
||||
|
||||
|
@ -142,8 +142,11 @@ public:
|
|||
void addDomainListener(DomainChangeListener* listener);
|
||||
void removeDomainListener(DomainChangeListener* listener);
|
||||
|
||||
void possiblyPingInactiveNodes();
|
||||
sockaddr* getNodeActiveSocketOrPing(Node* node);
|
||||
const HifiSockAddr* getNodeActiveSocketOrPing(Node* node);
|
||||
public slots:
|
||||
void sendDomainServerCheckIn();
|
||||
void pingInactiveNodes();
|
||||
void removeSilentNodes();
|
||||
private:
|
||||
static NodeList* _sharedInstance;
|
||||
|
||||
|
@ -160,25 +163,23 @@ private:
|
|||
void processKillNode(unsigned char* packetData, size_t dataBytes);
|
||||
|
||||
QString _domainHostname;
|
||||
QHostAddress _domainIP;
|
||||
unsigned short _domainPort;
|
||||
HifiSockAddr _domainSockAddr;
|
||||
Node** _nodeBuckets[MAX_NUM_NODES / NODES_PER_BUCKET];
|
||||
int _numNodes;
|
||||
UDPSocket _nodeSocket;
|
||||
QUdpSocket _nodeSocket;
|
||||
char _ownerType;
|
||||
char* _nodeTypesOfInterest;
|
||||
QUuid _ownerUUID;
|
||||
pthread_t removeSilentNodesThread;
|
||||
pthread_t checkInWithDomainServerThread;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
sockaddr* _assignmentServerSocket;
|
||||
QHostAddress _publicAddress;
|
||||
uint16_t _publicPort;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
HifiSockAddr _publicSockAddr;
|
||||
bool _hasCompletedInitialSTUNFailure;
|
||||
unsigned int _stunRequestsSinceSuccess;
|
||||
|
||||
void activateSocketFromNodeCommunication(sockaddr *nodeAddress);
|
||||
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
|
||||
void activateSocketFromNodeCommunication(const HifiSockAddr& nodeSockAddr);
|
||||
void timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData);
|
||||
|
||||
std::vector<NodeListHook*> _hooks;
|
||||
std::vector<DomainChangeListener*> _domainListeners;
|
||||
|
|
|
@ -63,6 +63,9 @@ int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsi
|
|||
int parentSections = numberOfThreeBitSectionsInCode(ancestorOctalCode);
|
||||
|
||||
int branchStartBit = parentSections * 3;
|
||||
// Note: this does not appear to be "multi-byte length code" safe. When octal codes are larger than 255 bytes
|
||||
// long, the length code is stored in two bytes. The "1" below appears to assume that the length is always one
|
||||
// byte long.
|
||||
return sectionValue(descendantOctalCode + 1 + (branchStartBit / 8), branchStartBit % 8);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
case PACKET_TYPE_DOMAIN:
|
||||
case PACKET_TYPE_DOMAIN_LIST_REQUEST:
|
||||
case PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY:
|
||||
return 1;
|
||||
return 2;
|
||||
|
||||
case PACKET_TYPE_VOXEL_QUERY:
|
||||
return 2;
|
||||
|
|
|
@ -44,7 +44,7 @@ PacketSender::PacketSender(PacketSenderNotify* notify, int packetsPerSecond) :
|
|||
}
|
||||
|
||||
|
||||
void PacketSender::queuePacketForSending(sockaddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
void PacketSender::queuePacketForSending(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
NetworkPacket packet(address, packetData, packetLength);
|
||||
lock();
|
||||
_packets.push_back(packet);
|
||||
|
@ -334,9 +334,9 @@ bool PacketSender::nonThreadedProcess() {
|
|||
unlock();
|
||||
|
||||
// send the packet through the NodeList...
|
||||
UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket();
|
||||
|
||||
nodeSocket->send(&temporary.getAddress(), temporary.getData(), temporary.getLength());
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) temporary.getData(), temporary.getLength(),
|
||||
temporary.getSockAddr().getAddress(),
|
||||
temporary.getSockAddr().getPort());
|
||||
packetsSentThisCall++;
|
||||
_packetsOverCheckInterval++;
|
||||
_totalPacketsSent++;
|
||||
|
|
|
@ -38,11 +38,11 @@ public:
|
|||
PacketSender(PacketSenderNotify* notify = NULL, int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND);
|
||||
|
||||
/// Add packet to outbound queue.
|
||||
/// \param sockaddr& address the destination address
|
||||
/// \param HifiSockAddr& address the destination address
|
||||
/// \param packetData pointer to data
|
||||
/// \param ssize_t packetLength size of data
|
||||
/// \thread any thread, typically the application thread
|
||||
void queuePacketForSending(sockaddr& address, unsigned char* packetData, ssize_t packetLength);
|
||||
void queuePacketForSending(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
void setPacketsPerSecond(int packetsPerSecond)
|
||||
{ _packetsPerSecond = std::max(MINIMUM_PACKETS_PER_SECOND, packetsPerSecond); }
|
||||
|
|
|
@ -16,9 +16,9 @@ ReceivedPacketProcessor::ReceivedPacketProcessor() {
|
|||
_dontSleep = false;
|
||||
}
|
||||
|
||||
void ReceivedPacketProcessor::queueReceivedPacket(sockaddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
void ReceivedPacketProcessor::queueReceivedPacket(const HifiSockAddr& address, unsigned char* packetData, ssize_t packetLength) {
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&address);
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(address);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ bool ReceivedPacketProcessor::process() {
|
|||
NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us
|
||||
_packets.erase(_packets.begin()); // remove the oldest packet
|
||||
unlock(); // let others add to the packets
|
||||
processPacket(temporary.getAddress(), temporary.getData(), temporary.getLength()); // process our temporary copy
|
||||
processPacket(temporary.getSockAddr(), temporary.getData(), temporary.getLength()); // process our temporary copy
|
||||
}
|
||||
return isStillRunning(); // keep running till they terminate us
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
/// \param packetData pointer to received data
|
||||
/// \param ssize_t packetLength size of received data
|
||||
/// \thread network receive thread
|
||||
void queueReceivedPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
void queueReceivedPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
/// Are there received packets waiting to be processed
|
||||
bool hasPacketsToProcess() const { return _packets.size() > 0; }
|
||||
|
@ -38,7 +38,7 @@ protected:
|
|||
/// \param packetData pointer to received data
|
||||
/// \param ssize_t packetLength size of received data
|
||||
/// \thread "this" individual processing thread
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) = 0;
|
||||
virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) = 0;
|
||||
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
|
|
32
libraries/shared/src/ThreadedAssignment.cpp
Normal file
32
libraries/shared/src/ThreadedAssignment.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// ThreadedAssignment.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/3/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ThreadedAssignment.h"
|
||||
|
||||
ThreadedAssignment::ThreadedAssignment(const unsigned char* dataBuffer, int numBytes) :
|
||||
Assignment(dataBuffer, numBytes),
|
||||
_isFinished(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ThreadedAssignment::setFinished(bool isFinished) {
|
||||
_isFinished = isFinished;
|
||||
|
||||
if (_isFinished) {
|
||||
emit finished();
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadedAssignment::checkInWithDomainServerOrExit() {
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
setFinished(true);
|
||||
} else {
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
}
|
34
libraries/shared/src/ThreadedAssignment.h
Normal file
34
libraries/shared/src/ThreadedAssignment.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ThreadedAssignment.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/3/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__ThreadedAssignment__
|
||||
#define __hifi__ThreadedAssignment__
|
||||
|
||||
#include "Assignment.h"
|
||||
|
||||
class ThreadedAssignment : public Assignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ThreadedAssignment(const unsigned char* dataBuffer, int numBytes);
|
||||
|
||||
void setFinished(bool isFinished);
|
||||
public slots:
|
||||
/// threaded run of assignment
|
||||
virtual void run() = 0;
|
||||
|
||||
virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) = 0;
|
||||
protected:
|
||||
bool _isFinished;
|
||||
private slots:
|
||||
void checkInWithDomainServerOrExit();
|
||||
signals:
|
||||
void finished();
|
||||
};
|
||||
|
||||
|
||||
#endif /* defined(__hifi__ThreadedAssignment__) */
|
|
@ -1,293 +0,0 @@
|
|||
//
|
||||
// UDPSocket.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Stephen Birarda on 1/28/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <cstdio>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtNetwork/QNetworkInterface>
|
||||
#include <QtNetwork/QHostAddress>
|
||||
|
||||
#include "Logging.h"
|
||||
#include "UDPSocket.h"
|
||||
|
||||
sockaddr_in destSockaddr, senderAddress;
|
||||
|
||||
bool socketMatch(const sockaddr* first, const sockaddr* second) {
|
||||
if (first != NULL && second != NULL) {
|
||||
// utility function that indicates if two sockets are equivalent
|
||||
|
||||
// currently only compares two IPv4 addresses
|
||||
// expandable to IPv6 by adding else if for AF_INET6
|
||||
|
||||
if (first->sa_family != second->sa_family) {
|
||||
// not the same family, can't be equal
|
||||
return false;
|
||||
} else if (first->sa_family == AF_INET) {
|
||||
const sockaddr_in *firstIn = (const sockaddr_in *) first;
|
||||
const sockaddr_in *secondIn = (const sockaddr_in *) second;
|
||||
|
||||
return firstIn->sin_addr.s_addr == secondIn->sin_addr.s_addr
|
||||
&& firstIn->sin_port == secondIn->sin_port;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int packSocket(unsigned char* packStore, in_addr_t inAddress, in_port_t networkOrderPort) {
|
||||
packStore[0] = inAddress >> 24;
|
||||
packStore[1] = inAddress >> 16;
|
||||
packStore[2] = inAddress >> 8;
|
||||
packStore[3] = inAddress;
|
||||
|
||||
packStore[4] = networkOrderPort >> 8;
|
||||
packStore[5] = networkOrderPort;
|
||||
|
||||
return 6; // could be dynamically more if we need IPv6
|
||||
}
|
||||
|
||||
int packSocket(unsigned char* packStore, sockaddr* socketToPack) {
|
||||
return packSocket(packStore, ((sockaddr_in*) socketToPack)->sin_addr.s_addr, ((sockaddr_in*) socketToPack)->sin_port);
|
||||
}
|
||||
|
||||
int unpackSocket(const unsigned char* packedData, sockaddr* unpackDestSocket) {
|
||||
sockaddr_in* destinationSocket = (sockaddr_in*) unpackDestSocket;
|
||||
destinationSocket->sin_family = AF_INET;
|
||||
destinationSocket->sin_addr.s_addr = (packedData[0] << 24) + (packedData[1] << 16) + (packedData[2] << 8) + packedData[3];
|
||||
destinationSocket->sin_port = (packedData[4] << 8) + packedData[5];
|
||||
return 6; // this could be more if we ever need IPv6
|
||||
}
|
||||
|
||||
void copySocketToEmptySocketPointer(sockaddr** destination, const sockaddr* source) {
|
||||
// create a new sockaddr or sockaddr_in depending on what type of address this is
|
||||
if (source->sa_family == AF_INET) {
|
||||
*destination = (sockaddr*) new sockaddr_in;
|
||||
memcpy(*destination, source, sizeof(sockaddr_in));
|
||||
} else {
|
||||
*destination = (sockaddr*) new sockaddr_in6;
|
||||
memcpy(*destination, source, sizeof(sockaddr_in6));
|
||||
}
|
||||
}
|
||||
|
||||
int getLocalAddress() {
|
||||
|
||||
static int localAddress = 0;
|
||||
|
||||
if (localAddress == 0) {
|
||||
foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
|
||||
if (interface.flags() & QNetworkInterface::IsUp
|
||||
&& interface.flags() & QNetworkInterface::IsRunning
|
||||
&& interface.flags() & ~QNetworkInterface::IsLoopBack) {
|
||||
// we've decided that this is the active NIC
|
||||
// enumerate it's addresses to grab the IPv4 address
|
||||
foreach(const QNetworkAddressEntry &entry, interface.addressEntries()) {
|
||||
// make sure it's an IPv4 address that isn't the loopback
|
||||
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) {
|
||||
qDebug("Node's local address is %s\n", entry.ip().toString().toLocal8Bit().constData());
|
||||
|
||||
// set our localAddress and break out
|
||||
localAddress = htonl(entry.ip().toIPv4Address());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localAddress != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return the looked up local address
|
||||
return localAddress;
|
||||
}
|
||||
|
||||
unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket) {
|
||||
if (socket != NULL) {
|
||||
char* copyBuffer = inet_ntoa(((sockaddr_in*) socket)->sin_addr);
|
||||
memcpy(addressBuffer, copyBuffer, strlen(copyBuffer));
|
||||
return htons(((sockaddr_in*) socket)->sin_port);
|
||||
} else {
|
||||
const char* unknownAddress = "Unknown";
|
||||
memcpy(addressBuffer, unknownAddress, strlen(unknownAddress));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sockaddr_in socketForHostnameAndHostOrderPort(const char* hostname, unsigned short port) {
|
||||
struct hostent* pHostInfo;
|
||||
sockaddr_in newSocket = {};
|
||||
|
||||
if ((pHostInfo = gethostbyname(hostname))) {
|
||||
memcpy(&newSocket.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length);
|
||||
}
|
||||
|
||||
if (port != 0) {
|
||||
newSocket.sin_port = htons(port);
|
||||
}
|
||||
|
||||
return newSocket;
|
||||
}
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short int listeningPort) :
|
||||
_listeningPort(listeningPort),
|
||||
blocking(true)
|
||||
{
|
||||
init();
|
||||
// create the socket
|
||||
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (handle <= 0) {
|
||||
qDebug("Failed to create socket.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
destSockaddr.sin_family = AF_INET;
|
||||
|
||||
// bind the socket to the passed listeningPort
|
||||
sockaddr_in bind_address;
|
||||
bind_address.sin_family = AF_INET;
|
||||
bind_address.sin_addr.s_addr = INADDR_ANY;
|
||||
bind_address.sin_port = htons((uint16_t) _listeningPort);
|
||||
|
||||
if (bind(handle, (const sockaddr*) &bind_address, sizeof(sockaddr_in)) < 0) {
|
||||
qDebug("Failed to bind socket to port %hu.\n", _listeningPort);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we requested an ephemeral port, get the actual port
|
||||
if (listeningPort == 0) {
|
||||
socklen_t addressLength = sizeof(sockaddr_in);
|
||||
getsockname(handle, (sockaddr*) &bind_address, &addressLength);
|
||||
_listeningPort = ntohs(bind_address.sin_port);
|
||||
}
|
||||
|
||||
const int DEFAULT_BLOCKING_SOCKET_TIMEOUT_USECS = 0.5 * 1000000;
|
||||
setBlockingReceiveTimeoutInUsecs(DEFAULT_BLOCKING_SOCKET_TIMEOUT_USECS);
|
||||
|
||||
qDebug("Created UDP Socket listening on %hd\n", _listeningPort);
|
||||
}
|
||||
|
||||
UDPSocket::~UDPSocket() {
|
||||
#ifdef _WIN32
|
||||
closesocket(handle);
|
||||
#else
|
||||
close(handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UDPSocket::init() {
|
||||
#ifdef _WIN32
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int err;
|
||||
|
||||
wVersionRequested = MAKEWORD( 2, 2 );
|
||||
|
||||
err = WSAStartup( wVersionRequested, &wsaData );
|
||||
if ( err != 0 ) {
|
||||
/* Tell the user that we could not find a usable */
|
||||
/* WinSock DLL. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Confirm that the WinSock DLL supports 2.2.*/
|
||||
/* Note that if the DLL supports versions later */
|
||||
/* than 2.2 in addition to 2.2, it will still return */
|
||||
/* 2.2 in wVersion since that is the version we */
|
||||
/* requested. */
|
||||
|
||||
if ( LOBYTE( wsaData.wVersion ) != 2 ||
|
||||
HIBYTE( wsaData.wVersion ) != 2 ) {
|
||||
/* Tell the user that we could not find a usable */
|
||||
/* WinSock DLL. */
|
||||
WSACleanup();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void UDPSocket::setBlocking(bool blocking) {
|
||||
this->blocking = blocking;
|
||||
|
||||
#ifdef _WIN32
|
||||
u_long mode = blocking ? 0 : 1;
|
||||
ioctlsocket(handle, FIONBIO, &mode);
|
||||
#else
|
||||
int flags = fcntl(handle, F_GETFL, 0);
|
||||
fcntl(handle, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK));
|
||||
#endif
|
||||
}
|
||||
|
||||
void UDPSocket::setBlockingReceiveTimeoutInUsecs(int timeoutUsecs) {
|
||||
struct timeval tv = {timeoutUsecs / 1000000, timeoutUsecs % 1000000};
|
||||
setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
|
||||
}
|
||||
|
||||
// Receive data on this socket with retrieving address of sender
|
||||
bool UDPSocket::receive(void* receivedData, ssize_t* receivedBytes) const {
|
||||
return receive((sockaddr*) &senderAddress, receivedData, receivedBytes);
|
||||
}
|
||||
|
||||
// Receive data on this socket with the address of the sender
|
||||
bool UDPSocket::receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const {
|
||||
|
||||
#ifdef _WIN32
|
||||
int addressSize = sizeof(*recvAddress);
|
||||
#else
|
||||
socklen_t addressSize = sizeof(*recvAddress);
|
||||
#endif
|
||||
*receivedBytes = recvfrom(handle, static_cast<char*>(receivedData), MAX_BUFFER_LENGTH_BYTES,
|
||||
0, recvAddress, &addressSize);
|
||||
|
||||
return (*receivedBytes > 0);
|
||||
}
|
||||
|
||||
int UDPSocket::send(sockaddr* destAddress, const void* data, size_t byteLength) const {
|
||||
if (destAddress) {
|
||||
// send data via UDP
|
||||
int sent_bytes = sendto(handle, (const char*)data, byteLength,
|
||||
0, (sockaddr *) destAddress, sizeof(sockaddr_in));
|
||||
|
||||
if (sent_bytes != byteLength) {
|
||||
qDebug("Failed to send packet: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return sent_bytes;
|
||||
} else {
|
||||
qDebug("UDPSocket send called with NULL destination address - Likely a node with no active socket.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
destSockaddr.sin_port = htons((uint16_t)destPort);
|
||||
|
||||
return send((sockaddr *)&destSockaddr, data, byteLength);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
//
|
||||
// UDPSocket.h
|
||||
// interface
|
||||
//
|
||||
// Created by Stephen Birarda on 1/28/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__UDPSocket__
|
||||
#define __interface__UDPSocket__
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#define MAX_BUFFER_LENGTH_BYTES 1500
|
||||
|
||||
class UDPSocket {
|
||||
public:
|
||||
UDPSocket(unsigned short int listeningPort);
|
||||
~UDPSocket();
|
||||
|
||||
bool init();
|
||||
unsigned short int getListeningPort() const { return _listeningPort; }
|
||||
|
||||
void setBlocking(bool blocking);
|
||||
bool isBlocking() const { return blocking; }
|
||||
void setBlockingReceiveTimeoutInUsecs(int timeoutUsecs);
|
||||
|
||||
int send(sockaddr* destAddress, 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;
|
||||
private:
|
||||
int handle;
|
||||
unsigned short int _listeningPort;
|
||||
bool blocking;
|
||||
};
|
||||
|
||||
bool socketMatch(const sockaddr* first, const sockaddr* second);
|
||||
int packSocket(unsigned char* packStore, in_addr_t inAddress, in_port_t networkOrderPort);
|
||||
int packSocket(unsigned char* packStore, sockaddr* socketToPack);
|
||||
int unpackSocket(const unsigned char* packedData, sockaddr* unpackDestSocket);
|
||||
void copySocketToEmptySocketPointer(sockaddr** destination, const sockaddr* source);
|
||||
int getLocalAddress();
|
||||
unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket);
|
||||
sockaddr_in socketForHostnameAndHostOrderPort(const char* hostname, unsigned short port = 0);
|
||||
|
||||
#endif /* defined(__interface__UDPSocket__) */
|
|
@ -145,11 +145,15 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
|
|||
}
|
||||
|
||||
// actually send it
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
packetSent = true;
|
||||
} else {
|
||||
// not enough room in the packet, send two packets
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
|
||||
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
|
||||
// there was nothing else to send.
|
||||
|
@ -169,8 +173,9 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
|
|||
truePacketsSent++;
|
||||
packetsSent++;
|
||||
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
|
||||
packetSent = true;
|
||||
|
||||
|
@ -191,8 +196,9 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
|
|||
// If there's actually a packet waiting, then send it.
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
// just send the voxel packet
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
packetSent = true;
|
||||
|
||||
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
|
||||
|
@ -518,7 +524,9 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
|
|||
envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength);
|
||||
}
|
||||
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), _tempOutputBuffer, envPacketLength);
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
trueBytesSent += envPacketLength;
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <Logging.h>
|
||||
|
@ -53,8 +54,10 @@ void attachVoxelNodeDataToNode(Node* newNode) {
|
|||
|
||||
VoxelServer* VoxelServer::_theInstance = NULL;
|
||||
|
||||
VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes),
|
||||
_serverTree(true) {
|
||||
VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) :
|
||||
ThreadedAssignment(dataBuffer, numBytes),
|
||||
_serverTree(true)
|
||||
{
|
||||
_argc = 0;
|
||||
_argv = NULL;
|
||||
|
||||
|
@ -495,6 +498,69 @@ void VoxelServer::parsePayload() {
|
|||
}
|
||||
}
|
||||
|
||||
void VoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
if (dataByteArray[0] == PACKET_TYPE_VOXEL_QUERY) {
|
||||
bool debug = false;
|
||||
if (debug) {
|
||||
qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow());
|
||||
}
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data());
|
||||
|
||||
// If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesPacketHeader,
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
Node* node = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (node) {
|
||||
nodeList->updateNodeWithData(node, senderSockAddr, (unsigned char *) dataByteArray.data(),
|
||||
dataByteArray.size());
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active socket for this node, but they're talking to us
|
||||
// this means they've heard from us and can reply, let's assume public is active
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
|
||||
nodeData->initializeVoxelSendThread(this);
|
||||
}
|
||||
}
|
||||
} else if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
|
||||
dataByteArray.size());
|
||||
}
|
||||
} else if (_voxelServerPacketProcessor &&
|
||||
(dataByteArray[0] == PACKET_TYPE_SET_VOXEL
|
||||
|| dataByteArray[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE
|
||||
|| dataByteArray[0] == PACKET_TYPE_ERASE_VOXEL)) {
|
||||
|
||||
|
||||
const char* messageName;
|
||||
switch (dataByteArray[0]) {
|
||||
case PACKET_TYPE_SET_VOXEL:
|
||||
messageName = "PACKET_TYPE_SET_VOXEL";
|
||||
break;
|
||||
case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE:
|
||||
messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE";
|
||||
break;
|
||||
case PACKET_TYPE_ERASE_VOXEL:
|
||||
messageName = "PACKET_TYPE_ERASE_VOXEL";
|
||||
break;
|
||||
}
|
||||
_voxelServerPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
|
||||
dataByteArray.size());
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(),
|
||||
dataByteArray.size());
|
||||
}
|
||||
}
|
||||
|
||||
//int main(int argc, const char * argv[]) {
|
||||
void VoxelServer::run() {
|
||||
|
||||
|
@ -647,12 +713,7 @@ void VoxelServer::run() {
|
|||
qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval);
|
||||
}
|
||||
|
||||
sockaddr senderAddress;
|
||||
|
||||
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
|
||||
ssize_t packetLength;
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
// set up our jurisdiction broadcaster...
|
||||
_jurisdictionSender = new JurisdictionSender(_jurisdiction);
|
||||
|
@ -679,82 +740,21 @@ void VoxelServer::run() {
|
|||
}
|
||||
qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n";
|
||||
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
QTimer* pingNodesTimer = new QTimer(this);
|
||||
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||
|
||||
// loop to send to nodes requesting data
|
||||
while (true) {
|
||||
// check for >= in case one gets past the goalie
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
// ping our inactive nodes to punch holes with them
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_VOXEL_QUERY) {
|
||||
bool debug = false;
|
||||
if (debug) {
|
||||
qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow());
|
||||
}
|
||||
|
||||
// If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
Node* node = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (node) {
|
||||
nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength);
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active socket for this node, but they're talking to us
|
||||
// this means they've heard from us and can reply, let's assume public is active
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
|
||||
nodeData->initializeVoxelSendThread(this);
|
||||
}
|
||||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
|
||||
}
|
||||
} else if (_voxelServerPacketProcessor &&
|
||||
(packetData[0] == PACKET_TYPE_SET_VOXEL
|
||||
|| packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE
|
||||
|| packetData[0] == PACKET_TYPE_ERASE_VOXEL)) {
|
||||
|
||||
|
||||
const char* messageName;
|
||||
switch (packetData[0]) {
|
||||
case PACKET_TYPE_SET_VOXEL:
|
||||
messageName = "PACKET_TYPE_SET_VOXEL";
|
||||
break;
|
||||
case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE:
|
||||
messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE";
|
||||
break;
|
||||
case PACKET_TYPE_ERASE_VOXEL:
|
||||
messageName = "PACKET_TYPE_ERASE_VOXEL";
|
||||
break;
|
||||
}
|
||||
_voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
|
||||
}
|
||||
}
|
||||
while (!_isFinished) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
// call NodeList::clear() so that all of our node specific objects, including our sending threads, are
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <QDateTime>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <Assignment.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
#include <EnvironmentData.h>
|
||||
|
||||
#include "civetweb.h"
|
||||
|
@ -26,14 +26,13 @@
|
|||
#include "VoxelServerPacketProcessor.h"
|
||||
|
||||
/// Handles assignments of type VoxelServer - sending voxels to various clients.
|
||||
class VoxelServer : public Assignment {
|
||||
class VoxelServer : public ThreadedAssignment {
|
||||
public:
|
||||
VoxelServer(const unsigned char* dataBuffer, int numBytes);
|
||||
|
||||
~VoxelServer();
|
||||
|
||||
/// runs the voxel server assignment
|
||||
void run();
|
||||
|
||||
|
||||
/// allows setting of run arguments
|
||||
void setArguments(int argc, char** argv);
|
||||
|
@ -59,7 +58,11 @@ public:
|
|||
bool isInitialLoadComplete() const { return (_voxelPersistThread) ? _voxelPersistThread->isInitialLoadComplete() : true; }
|
||||
time_t* getLoadCompleted() { return (_voxelPersistThread) ? _voxelPersistThread->getLoadCompleted() : NULL; }
|
||||
uint64_t getLoadElapsedTime() const { return (_voxelPersistThread) ? _voxelPersistThread->getLoadElapsedTime() : 0; }
|
||||
public slots:
|
||||
/// runs the voxel server assignment
|
||||
void run();
|
||||
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
private:
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
|
|
|
@ -39,7 +39,8 @@ void VoxelServerPacketProcessor::resetStats() {
|
|||
}
|
||||
|
||||
|
||||
void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
void VoxelServerPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr,
|
||||
unsigned char* packetData, ssize_t packetLength) {
|
||||
|
||||
bool debugProcessPacket = _myServer->wantsVerboseDebug();
|
||||
|
||||
|
@ -135,7 +136,7 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
|
|||
}
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* senderNode = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
QUuid& nodeUUID = DEFAULT_NODE_ID_REF;
|
||||
if (senderNode) {
|
||||
senderNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
@ -170,7 +171,7 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
|
|||
_myServer->getServerTree().unlock();
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
|
||||
|
||||
protected:
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength);
|
||||
|
||||
private:
|
||||
void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
|
||||
|
|
|
@ -123,7 +123,7 @@ void VoxelEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned ch
|
|||
if (node->getType() == NODE_TYPE_VOXEL_SERVER &&
|
||||
((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
|
||||
if (nodeList->getNodeActiveSocketOrPing(&(*node))) {
|
||||
sockaddr* nodeAddress = node->getActiveSocket();
|
||||
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
||||
queuePacketForSending(*nodeAddress, buffer, length);
|
||||
|
||||
// debugging output...
|
||||
|
|
|
@ -5,9 +5,13 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
|||
|
||||
set(TARGET_NAME pairing-server)
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network)
|
||||
|
||||
# link the shared hifi library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
|
@ -11,17 +11,20 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <UDPSocket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
|
||||
const int PAIRING_SERVER_LISTEN_PORT = 7247;
|
||||
const int MAX_PACKET_SIZE_BYTES = 1400;
|
||||
|
||||
struct PairableDevice {
|
||||
char identifier[64];
|
||||
char name[64];
|
||||
sockaddr_in sendingSocket;
|
||||
sockaddr_in localSocket;
|
||||
HifiSockAddr sendingSocket;
|
||||
HifiSockAddr localSocket;
|
||||
};
|
||||
|
||||
struct RequestingClient {
|
||||
|
@ -29,7 +32,7 @@ struct RequestingClient {
|
|||
int port;
|
||||
};
|
||||
|
||||
UDPSocket serverSocket(PAIRING_SERVER_LISTEN_PORT);
|
||||
QUdpSocket serverSocket;
|
||||
PairableDevice* lastDevice = NULL;
|
||||
RequestingClient* lastClient = NULL;
|
||||
|
||||
|
@ -47,17 +50,21 @@ void sendLastClientToLastDevice() {
|
|||
char pairData[INET_ADDRSTRLEN + 6] = {};
|
||||
int bytesWritten = sprintf(pairData, "%s:%d", ::lastClient->address, ::lastClient->port);
|
||||
|
||||
::serverSocket.send((sockaddr*) &::lastDevice->sendingSocket, pairData, bytesWritten);
|
||||
::serverSocket.writeDatagram(pairData, bytesWritten,
|
||||
::lastDevice->sendingSocket.getAddress(), ::lastDevice->sendingSocket.getPort());
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
|
||||
sockaddr_in senderSocket;
|
||||
serverSocket.bind(QHostAddress::LocalHost, PAIRING_SERVER_LISTEN_PORT);
|
||||
|
||||
HifiSockAddr senderSockAddr;
|
||||
char senderData[MAX_PACKET_SIZE_BYTES] = {};
|
||||
ssize_t receivedBytes = 0;
|
||||
|
||||
while (true) {
|
||||
if (::serverSocket.receive((sockaddr*) &senderSocket, &senderData, &receivedBytes)) {
|
||||
if (::serverSocket.hasPendingDatagrams()
|
||||
&& ::serverSocket.readDatagram(senderData, MAX_PACKET_SIZE_BYTES,
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer())) {
|
||||
if (senderData[0] == 'A') {
|
||||
// this is a device reporting itself as available
|
||||
|
||||
|
@ -76,12 +83,11 @@ int main(int argc, const char* argv[]) {
|
|||
// if we have fewer than 3 matches the packet wasn't properly formatted
|
||||
|
||||
// setup the localSocket for the pairing device
|
||||
tempDevice.localSocket.sin_family = AF_INET;
|
||||
inet_pton(AF_INET, deviceAddress, &::lastDevice);
|
||||
tempDevice.localSocket.sin_port = socketPort;
|
||||
tempDevice.localSocket.setAddress(QHostAddress(deviceAddress));
|
||||
tempDevice.localSocket.setPort(socketPort);
|
||||
|
||||
// store this device's sending socket so we can talk back to it
|
||||
tempDevice.sendingSocket = senderSocket;
|
||||
tempDevice.sendingSocket = senderSockAddr;
|
||||
|
||||
// push this new device into the vector
|
||||
printf("New last device is %s (%s) at %s:%d\n",
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
set(TARGET_NAME space-server)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
|
@ -1,2 +0,0 @@
|
|||
0 00000100001011101110 domain4.highfidelity.co domain4
|
||||
0 00000100110100010001 domain3.highfidelity.co domain3
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// TreeNode.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/13/13.
|
||||
//
|
||||
//
|
||||
|
||||
#include "TreeNode.h"
|
||||
|
||||
std::string EMPTY_STRING = "";
|
||||
|
||||
TreeNode::TreeNode() {
|
||||
for (int i = 0; i < CHILDREN_PER_NODE; ++i) {
|
||||
child[i] = NULL;
|
||||
}
|
||||
|
||||
hostname = NULL;
|
||||
nickname = NULL;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// TreeNode.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/13/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __hifi__TreeNode__
|
||||
#define __hifi__TreeNode__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
const int CHILDREN_PER_NODE = 8;
|
||||
|
||||
class TreeNode {
|
||||
public:
|
||||
TreeNode();
|
||||
|
||||
TreeNode *child[CHILDREN_PER_NODE];
|
||||
char *hostname;
|
||||
char *nickname;
|
||||
int domain_id;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__TreeNode__) */
|
|
@ -1,160 +0,0 @@
|
|||
//
|
||||
// main.cpp
|
||||
// space
|
||||
//
|
||||
// Created by Leonardo Murillo on 2/6/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "TreeNode.h"
|
||||
#include "UDPSocket.h"
|
||||
|
||||
const char *CONFIG_FILE = "/Users/birarda/code/worklist/checkouts/hifi/space/example.data.txt";
|
||||
const unsigned short SPACE_LISTENING_PORT = 55551;
|
||||
const short MAX_NAME_LENGTH = 255;
|
||||
|
||||
const char ROOT_HOSTNAME[] = "root.highfidelity.co";
|
||||
const char ROOT_NICKNAME[] = "root";
|
||||
|
||||
const size_t PACKET_LENGTH_BYTES = 1024;
|
||||
|
||||
sockaddr_in destAddress;
|
||||
socklen_t destLength = sizeof(destAddress);
|
||||
|
||||
char *lastKnownHostname;
|
||||
|
||||
TreeNode rootNode;
|
||||
UDPSocket spaceSocket(SPACE_LISTENING_PORT);
|
||||
|
||||
TreeNode *findOrCreateNode(int lengthInBits,
|
||||
unsigned char *addressBytes,
|
||||
char *hostname,
|
||||
char *nickname,
|
||||
int domainID) {
|
||||
|
||||
TreeNode *currentNode = &rootNode;
|
||||
|
||||
for (int i = 0; i < lengthInBits; i += 3) {
|
||||
unsigned char octet;
|
||||
|
||||
if (i%8 < 6) {
|
||||
octet = addressBytes[i/8] << i%8 >> (5);
|
||||
} else {
|
||||
octet = (addressBytes[i/8] << i >> (11 - i)) | (addressBytes[i/8 + 1] >> (11 - i + 2));
|
||||
}
|
||||
|
||||
if (currentNode->child[octet] == NULL) {
|
||||
currentNode->child[octet] = new TreeNode;
|
||||
} else if (currentNode->child[octet]->hostname != NULL) {
|
||||
lastKnownHostname = currentNode->child[octet]->hostname;
|
||||
}
|
||||
|
||||
currentNode = currentNode->child[octet];
|
||||
}
|
||||
|
||||
if (currentNode->hostname == NULL) {
|
||||
currentNode->hostname = hostname;
|
||||
currentNode->nickname = nickname;
|
||||
}
|
||||
|
||||
return currentNode;
|
||||
};
|
||||
|
||||
bool loadSpaceData(void) {
|
||||
FILE *configFile = std::fopen(CONFIG_FILE, "r");
|
||||
char formatString[10];
|
||||
|
||||
if (configFile == NULL) {
|
||||
std::cout << "Unable to load config file!\n";
|
||||
return false;
|
||||
} else {
|
||||
char *lengthBitString = new char[8];
|
||||
int itemsRead = 0;
|
||||
|
||||
while ((itemsRead = fscanf(configFile, "0 %8c", lengthBitString)) > 0) {
|
||||
|
||||
// calculate the number of bits in the address and bits required for padding
|
||||
unsigned long threeBitCodes = strtoul(lengthBitString, NULL, 2);
|
||||
int bitsInAddress = threeBitCodes * 3;
|
||||
int paddingBits = 8 - (bitsInAddress % 8);
|
||||
int addressByteLength = (bitsInAddress + paddingBits) / 8;
|
||||
|
||||
// create an unsigned char * to hold the padded address
|
||||
unsigned char *paddedAddress = new unsigned char[addressByteLength];
|
||||
|
||||
char *fullByteBitString = new char[8];
|
||||
|
||||
for (int c = 0; c < addressByteLength; c++) {
|
||||
if (c + 1 == addressByteLength && paddingBits > 0) {
|
||||
// this is the last byte, and we need some padding bits
|
||||
// pull as many bits as are left
|
||||
int goodBits = 8 - paddingBits;
|
||||
sprintf(formatString, "%%%dc", goodBits);
|
||||
itemsRead = fscanf(configFile, formatString, fullByteBitString);
|
||||
|
||||
// fill out the rest with zeros
|
||||
memset(fullByteBitString + goodBits, '0', paddingBits);
|
||||
} else {
|
||||
// pull 8 bits (which will be one byte) from the file
|
||||
itemsRead = fscanf(configFile, "%8c", fullByteBitString);
|
||||
}
|
||||
|
||||
// set the corresponding value in the unsigned char array
|
||||
*(paddedAddress + c) = strtoul(fullByteBitString, NULL, 2);
|
||||
}
|
||||
|
||||
char *nodeHostname = new char[MAX_NAME_LENGTH];
|
||||
char *nodeNickname = new char[MAX_NAME_LENGTH];
|
||||
itemsRead = fscanf(configFile, "%s %s\n", nodeHostname, nodeNickname);
|
||||
|
||||
findOrCreateNode(bitsInAddress, paddedAddress, nodeHostname, nodeNickname, 0);
|
||||
}
|
||||
|
||||
std::fclose(configFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, const char *argv[]) {
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
unsigned char packetData[PACKET_LENGTH_BYTES];
|
||||
ssize_t receivedBytes = 0;
|
||||
|
||||
rootNode.hostname = new char[MAX_NAME_LENGTH];
|
||||
rootNode.nickname = new char[MAX_NAME_LENGTH];
|
||||
|
||||
strcpy(rootNode.hostname, ROOT_HOSTNAME);
|
||||
strcpy(rootNode.nickname, ROOT_NICKNAME);
|
||||
|
||||
loadSpaceData();
|
||||
|
||||
std::cout << "[DEBUG] Listening for Datagrams" << std::endl;
|
||||
|
||||
while (true) {
|
||||
if (spaceSocket.receive((sockaddr *)&destAddress, &packetData, &receivedBytes)) {
|
||||
unsigned long lengthInBits;
|
||||
lengthInBits = packetData[0] * 3;
|
||||
|
||||
unsigned char addressData[sizeof(packetData)-1];
|
||||
for (int i = 0; i < sizeof(packetData)-1; ++i) {
|
||||
addressData[i] = packetData[i+1];
|
||||
}
|
||||
|
||||
TreeNode *thisNode = findOrCreateNode(lengthInBits, addressData, NULL, NULL, 0);
|
||||
char *hostnameToSend = (thisNode->hostname == NULL)
|
||||
? lastKnownHostname
|
||||
: thisNode->hostname;
|
||||
|
||||
spaceSocket.send((sockaddr *)&destAddress, &hostnameToSend, sizeof(hostnameToSend));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue