merge from upstream

This commit is contained in:
Seth Alves 2015-07-21 09:51:16 -07:00
commit cdc26526b4
291 changed files with 6022 additions and 10079 deletions

View file

@ -19,7 +19,7 @@
#include <AvatarHashMap.h>
#include <NetworkAccessManager.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <ResourceCache.h>
#include <SoundCache.h>
#include <UUID.h>
@ -32,7 +32,7 @@
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
Agent::Agent(const QByteArray& packet) :
Agent::Agent(NLPacket& packet) :
ThreadedAssignment(packet),
_entityEditSender(),
_receivedAudioStream(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES,
@ -42,111 +42,82 @@ Agent::Agent(const QByteArray& packet) :
{
// be the parent of the script engine so it gets moved when we do
_scriptEngine.setParent(this);
_scriptEngine.setIsAgent(true);
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<SoundCache>();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes(
{ PacketType::MixedAudio, PacketType::SilentAudioFrame },
this, "handleAudioPacket");
packetReceiver.registerListenerForTypes(
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket");
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
}
void Agent::readPendingDatagrams() {
QByteArray receivedPacket;
HifiSockAddr senderSockAddr;
auto nodeList = DependencyManager::get<NodeList>();
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
PacketType datagramPacketType = packetTypeForPacket(receivedPacket);
void Agent::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
auto packetType = packet->getType();
if (packetType == PacketType::OctreeStats) {
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(packet, senderNode);
if (packet->getPayloadSize() > statsMessageLength) {
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength;
if (datagramPacketType == PacketTypeJurisdiction) {
int headerBytes = numBytesForPacketHeader(receivedPacket);
SharedNodePointer matchedNode = nodeList->sendingNodeForPacket(receivedPacket);
if (matchedNode) {
// PacketType_JURISDICTION, first byte is the node type...
switch (receivedPacket[headerBytes]) {
case NodeType::EntityServer:
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
queueReceivedPacket(matchedNode, receivedPacket);
break;
}
}
} else if (datagramPacketType == PacketTypeOctreeStats
|| datagramPacketType == PacketTypeEntityData
|| datagramPacketType == PacketTypeEntityErase
) {
// Make sure our Node and NodeList knows we've heard from this node.
SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket);
sourceNode->setLastHeardMicrostamp(usecTimestampNow());
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[piggyBackedSizeWithHeader]);
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader);
QByteArray mutablePacket = receivedPacket;
int messageLength = mutablePacket.size();
if (datagramPacketType == PacketTypeOctreeStats) {
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(mutablePacket, sourceNode);
if (messageLength > statsMessageLength) {
mutablePacket = mutablePacket.mid(statsMessageLength);
// TODO: this needs to be fixed, the goal is to test the packet version for the piggyback, but
// this is testing the version and hash of the original packet
// need to use numBytesArithmeticCodingFromBuffer()...
if (!DependencyManager::get<NodeList>()->packetVersionAndHashMatch(receivedPacket)) {
return; // bail since piggyback data doesn't match our versioning
}
} else {
return; // bail since no piggyback data
}
datagramPacketType = packetTypeForPacket(mutablePacket);
} // fall through to piggyback message
if (datagramPacketType == PacketTypeEntityData || datagramPacketType == PacketTypeEntityErase) {
_entityViewer.processDatagram(mutablePacket, sourceNode);
}
} else if (datagramPacketType == PacketTypeMixedAudio || datagramPacketType == PacketTypeSilentAudioFrame) {
_receivedAudioStream.parseData(receivedPacket);
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
_receivedAudioStream.clearBuffer();
// let this continue through to the NodeList so it updates last heard timestamp
// for the sending audio mixer
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, receivedPacket);
} else if (datagramPacketType == PacketTypeBulkAvatarData
|| datagramPacketType == PacketTypeAvatarIdentity
|| datagramPacketType == PacketTypeAvatarBillboard
|| datagramPacketType == PacketTypeKillAvatar) {
// let the avatar hash map process it
DependencyManager::get<AvatarHashMap>()->processAvatarMixerDatagram(receivedPacket, nodeList->sendingNodeForPacket(receivedPacket));
// let this continue through to the NodeList so it updates last heard timestamp
// for the sending avatar-mixer
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, receivedPacket);
} else {
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, receivedPacket);
}
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, packet->getSenderSockAddr());
packet = QSharedPointer<NLPacket>(newPacket.release());
} else {
return; // bail since no piggyback data
}
packetType = packet->getType();
} // fall through to piggyback message
if (packetType == PacketType::EntityData || packetType == PacketType::EntityErase) {
_entityViewer.processDatagram(*packet, senderNode);
}
}
void Agent::handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
NodeType_t nodeType;
packet->peekPrimitive(&nodeType);
// PacketType_JURISDICTION, first byte is the node type...
if (nodeType == NodeType::EntityServer) {
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
queueReceivedPacket(packet, senderNode);
}
}
void Agent::handleAudioPacket(QSharedPointer<NLPacket> packet) {
_receivedAudioStream.parseData(*packet);
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
_receivedAudioStream.clearBuffer();
}
const QString AGENT_LOGGING_NAME = "agent";
void Agent::run() {
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
auto nodeList = DependencyManager::get<NodeList>();
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet()
<< NodeType::AudioMixer
<< NodeType::AvatarMixer
<< NodeType::EntityServer
);
// figure out the URL for the script for this agent assignment
QUrl scriptURL;
if (_payload.isEmpty()) {
@ -157,29 +128,29 @@ void Agent::run() {
} else {
scriptURL = QUrl(_payload);
}
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(scriptURL);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
QNetworkDiskCache* cache = new QNetworkDiskCache();
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
networkAccessManager.setCache(cache);
qDebug() << "Downloading script at" << scriptURL.toString();
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QString scriptContents(reply->readAll());
delete reply;
qDebug() << "Downloaded script:" << scriptContents;
// setup an Avatar for the script to use
ScriptableAvatar scriptedAvatar(&_scriptEngine);
scriptedAvatar.setForceFaceTrackerConnected(true);
@ -187,16 +158,25 @@ void Agent::run() {
// call model URL setters with empty URLs so our avatar, if user, will have the default models
scriptedAvatar.setFaceModelURL(QUrl());
scriptedAvatar.setSkeletonModelURL(QUrl());
// give this AvatarData object to the script engine
_scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
_scriptEngine.setAvatarHashMap(DependencyManager::get<AvatarHashMap>().data(), "AvatarList");
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
_scriptEngine.setAvatarHashMap(avatarHashMap.data(), "AvatarList");
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket");
packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar");
packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket");
packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket");
// register ourselves to the script engine
_scriptEngine.registerGlobalObject("Agent", this);
_scriptEngine.init(); // must be done before we set up the viewers
_scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();

View file

@ -35,7 +35,7 @@ class Agent : public ThreadedAssignment {
Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream)
Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness)
public:
Agent(const QByteArray& packet);
Agent(NLPacket& packet);
void setIsAvatar(bool isAvatar) { QMetaObject::invokeMethod(&_scriptEngine, "setIsAvatar", Q_ARG(bool, isAvatar)); }
bool isAvatar() const { return _scriptEngine.isAvatar(); }
@ -52,9 +52,13 @@ public:
public slots:
void run();
void readPendingDatagrams();
void playAvatarSound(Sound* avatarSound) { _scriptEngine.setAvatarSound(avatarSound); }
private slots:
void handleAudioPacket(QSharedPointer<NLPacket> packet);
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
private:
ScriptEngine _scriptEngine;
EntityEditPacketSender _entityEditSender;

View file

@ -26,7 +26,7 @@
#include <LogUtils.h>
#include <LimitedNodeList.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include <ShutdownEventListener.h>
#include <SoundCache.h>
@ -49,23 +49,28 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
LogUtils::init();
QSettings::setDefaultFormat(QSettings::IniFormat);
// create a NodeList as an unassigned client
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned); // Order is important
// create a NodeList as an unassigned client, must be after addressManager
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned);
auto animationCache = DependencyManager::set<AnimationCache>();
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>();
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
// make up a uuid for this child so the parent can tell us apart. This id will be changed
// when the domain server hands over an assignment.
QUuid nodeUUID = QUuid::createUuid();
nodeList->setSessionUUID(nodeUUID);
// setup a thread for the NodeList and its PacketReceiver
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread");
nodeThread->start();
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
// put the NodeList on the node thread
nodeList->moveToThread(nodeThread);
// set the logging target to the the CHILD_TARGET_NAME
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
@ -104,9 +109,6 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
_requestTimer.start(ASSIGNMENT_REQUEST_INTERVAL_MSECS);
// connect our readPendingDatagrams method to the readyRead() signal of the socket
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
// connections to AccountManager for authentication
connect(&AccountManager::getInstance(), &AccountManager::authRequired,
this, &AssignmentClient::handleAuthenticationRequest);
@ -121,11 +123,12 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;
// Hook up a timer to send this child's status to the Monitor once per second
setUpStatsToMonitor();
setUpStatusToMonitor();
}
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::CreateAssignment, this, "handleCreateAssignmentPacket");
packetReceiver.registerListener(PacketType::StopNode, this, "handleStopNodePacket");
}
void AssignmentClient::stopAssignmentClient() {
qDebug() << "Forced stop of assignment-client.";
@ -151,6 +154,16 @@ void AssignmentClient::stopAssignmentClient() {
}
}
AssignmentClient::~AssignmentClient() {
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
// remove the NodeList from the DependencyManager
DependencyManager::destroy<NodeList>();
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
}
void AssignmentClient::aboutToQuit() {
stopAssignmentClient();
@ -160,23 +173,28 @@ void AssignmentClient::aboutToQuit() {
}
void AssignmentClient::setUpStatsToMonitor() {
void AssignmentClient::setUpStatusToMonitor() {
// send a stats packet every 1 seconds
connect(&_statsTimerACM, &QTimer::timeout, this, &AssignmentClient::sendStatsPacketToACM);
connect(&_statsTimerACM, &QTimer::timeout, this, &AssignmentClient::sendStatusPacketToACM);
_statsTimerACM.start(1000);
}
void AssignmentClient::sendStatsPacketToACM() {
void AssignmentClient::sendStatusPacketToACM() {
// tell the assignment client monitor what this assignment client is doing (if anything)
QJsonObject statsObject;
auto nodeList = DependencyManager::get<NodeList>();
quint8 assignmentType = Assignment::Type::AllTypes;
if (_currentAssignment) {
statsObject["assignment_type"] = _currentAssignment->getTypeName();
} else {
statsObject["assignment_type"] = "none";
assignmentType = _currentAssignment->getType();
}
nodeList->sendStats(statsObject, _assignmentClientMonitorSocket);
auto statusPacket = NLPacket::create(PacketType::AssignmentClientStatus, sizeof(assignmentType) + NUM_BYTES_RFC4122_UUID);
statusPacket->write(_childAssignmentUUID.toRfc4122());
statusPacket->writePrimitive(assignmentType);
nodeList->sendPacket(std::move(statusPacket), _assignmentClientMonitorSocket);
}
void AssignmentClient::sendAssignmentRequest() {
@ -206,85 +224,64 @@ void AssignmentClient::sendAssignmentRequest() {
}
}
void AssignmentClient::readPendingDatagrams() {
auto nodeList = DependencyManager::get<NodeList>();
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> packet) {
qDebug() << "Received a PacketType::CreateAssignment - attempting to unpack.";
QByteArray receivedPacket;
HifiSockAddr senderSockAddr;
// construct the deployed assignment from the packet data
_currentAssignment = AssignmentFactory::unpackAssignment(*packet);
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
if (_currentAssignment) {
qDebug() << "Received an assignment -" << *_currentAssignment;
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) {
auto nodeList = DependencyManager::get<NodeList>();
qDebug() << "Received a PacketTypeCreateAssignment - attempting to unpack.";
// switch our DomainHandler hostname and port to whoever sent us the assignment
// construct the deployed assignment from the packet data
_currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket);
nodeList->getDomainHandler().setSockAddr(packet->getSenderSockAddr(), _assignmentServerHostname);
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
if (_currentAssignment) {
qDebug() << "Received an assignment -" << *_currentAssignment;
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
// switch our DomainHandler hostname and port to whoever sent us the assignment
// start the deployed assignment
QThread* workerThread = new QThread;
workerThread->setObjectName("ThreadedAssignment Worker");
nodeList->getDomainHandler().setSockAddr(senderSockAddr, _assignmentServerHostname);
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
// Once the ThreadedAssignment says it is finished - we ask it to deleteLater
// This is a queued connection so that it is put into the event loop to be processed by the worker
// thread when it is ready.
connect(_currentAssignment.data(), &ThreadedAssignment::finished, _currentAssignment.data(),
&ThreadedAssignment::deleteLater, Qt::QueuedConnection);
// start the deployed assignment
QThread* workerThread = new QThread;
workerThread->setObjectName("ThreadedAssignment Worker");
// once it is deleted, we quit the worker thread
connect(_currentAssignment.data(), &ThreadedAssignment::destroyed, workerThread, &QThread::quit);
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
// have the worker thread remove itself once it is done
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
// Once the ThreadedAssignment says it is finished - we ask it to deleteLater
// This is a queued connection so that it is put into the event loop to be processed by the worker
// thread when it is ready.
connect(_currentAssignment.data(), &ThreadedAssignment::finished, _currentAssignment.data(),
&ThreadedAssignment::deleteLater, Qt::QueuedConnection);
// once the worker thread says it is done, we consider the assignment completed
connect(workerThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted);
// once it is deleted, we quit the worker thread
connect(_currentAssignment.data(), &ThreadedAssignment::destroyed, workerThread, &QThread::quit);
_currentAssignment->moveToThread(workerThread);
// have the worker thread remove itself once it is done
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
// Starts an event loop, and emits workerThread->started()
workerThread->start();
} else {
qDebug() << "Received an assignment that could not be unpacked. Re-requesting.";
}
}
// once the worker thread says it is done, we consider the assignment completed
connect(workerThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted);
void AssignmentClient::handleStopNodePacket(QSharedPointer<NLPacket> packet) {
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
qDebug() << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode.";
_currentAssignment->moveToThread(workerThread);
// move the NodeList to the thread used for the _current assignment
nodeList->moveToThread(workerThread);
// let the assignment handle the incoming datagrams for its duration
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(),
&ThreadedAssignment::readPendingDatagrams);
// Starts an event loop, and emits workerThread->started()
workerThread->start();
} else {
qDebug() << "Received an assignment that could not be unpacked. Re-requesting.";
}
} else if (packetTypeForPacket(receivedPacket) == PacketTypeStopNode) {
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
qDebug() << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketTypeStopNode.";
QCoreApplication::quit();
} else {
qDebug() << "Got a stop packet from other than localhost.";
}
} else {
// have the NodeList attempt to handle it
nodeList->processNodeData(senderSockAddr, receivedPacket);
}
}
QCoreApplication::quit();
} else {
qDebug() << "Got a stop packet from other than localhost.";
}
}
@ -327,8 +324,8 @@ void AssignmentClient::assignmentCompleted() {
auto nodeList = DependencyManager::get<NodeList>();
// have us handle incoming NodeList datagrams again, and make sure our ThreadedAssignment isn't handling them
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
// tell the packet receiver to stop dropping packets
nodeList->getPacketReceiver().setShouldDropPackets(false);
// reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NodeType::Unassigned);

View file

@ -22,23 +22,26 @@ class QSharedMemory;
class AssignmentClient : public QObject {
Q_OBJECT
public:
AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
quint16 assignmentMonitorPort);
~AssignmentClient();
private slots:
void sendAssignmentRequest();
void readPendingDatagrams();
void assignmentCompleted();
void handleAuthenticationRequest();
void sendStatsPacketToACM();
void sendStatusPacketToACM();
void stopAssignmentClient();
public slots:
void aboutToQuit();
private slots:
void handleCreateAssignmentPacket(QSharedPointer<NLPacket> packet);
void handleStopNodePacket(QSharedPointer<NLPacket> packet);
private:
void setUpStatsToMonitor();
void setUpStatusToMonitor();
Assignment _requestAssignment;
QPointer<ThreadedAssignment> _currentAssignment;
@ -46,6 +49,7 @@ private:
HifiSockAddr _assignmentServerSocket;
QTimer _requestTimer; // timer for requesting and assignment
QTimer _statsTimerACM; // timer for sending stats to assignment client monitor
QUuid _childAssignmentUUID = QUuid::createUuid();
protected:
HifiSockAddr _assignmentClientMonitorSocket;

View file

@ -184,15 +184,15 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
if (numForks || minForks || maxForks) {
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
requestAssignmentType, assignmentPool,
walletUUID, assignmentServerHostname,
walletUUID, assignmentServerHostname,
assignmentServerPort);
monitor->setParent(this);
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
} else {
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool,
walletUUID, assignmentServerHostname,
walletUUID, assignmentServerHostname,
assignmentServerPort, monitorPort);
client->setParent(this);
connect(this, &QCoreApplication::aboutToQuit, client, &AssignmentClient::aboutToQuit);

View file

@ -1,8 +1,19 @@
//
// AssignmentClientChildData.cpp
// assignment-client/src
//
// Created by Stephen Birarda on 07/13/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AssignmentClientChildData.h"
AssignmentClientChildData::AssignmentClientChildData(QString childType) :
AssignmentClientChildData::AssignmentClientChildData(Assignment::Type childType) :
_childType(childType)
{
}

View file

@ -16,17 +16,14 @@
class AssignmentClientChildData : public NodeData {
public:
AssignmentClientChildData(QString childType);
public:
AssignmentClientChildData(Assignment::Type childType);
QString getChildType() { return _childType; }
void setChildType(QString childType) { _childType = childType; }
Assignment::Type getChildType() { return _childType; }
void setChildType(Assignment::Type childType) { _childType = childType; }
// implement parseData to return 0 so we can be a subclass of NodeData
int parseData(const QByteArray& packet) { return 0; }
private:
QString _childType;
private:
Assignment::Type _childType;
};
#endif // hifi_AssignmentClientChildData_h

View file

@ -14,14 +14,13 @@
#include <AddressManager.h>
#include <JSONBreakableMarshal.h>
#include <LogHandler.h>
#include <udt/PacketHeaders.h>
#include "AssignmentClientMonitor.h"
#include "AssignmentClientApp.h"
#include "AssignmentClientChildData.h"
#include "PacketHeaders.h"
#include "SharedUtil.h"
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
const int WAIT_FOR_CHILD_MSECS = 1000;
@ -53,7 +52,8 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<LimitedNodeList>();
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClientMonitor::readPendingDatagrams);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
// use QProcess to fork off a process for each of the child assignment clients
for (unsigned int i = 0; i < _numAssignmentClientForks; i++) {
@ -172,10 +172,10 @@ void AssignmentClientMonitor::checkSpares() {
nodeList->removeSilentNodes();
nodeList->eachNode([&](const SharedNodePointer& node) {
AssignmentClientChildData *childData = static_cast<AssignmentClientChildData*>(node->getLinkedData());
AssignmentClientChildData* childData = static_cast<AssignmentClientChildData*>(node->getLinkedData());
totalCount ++;
if (childData->getChildType() == "none") {
spareCount ++;
if (childData->getChildType() == Assignment::Type::AllTypes) {
++spareCount;
aSpareId = node->getUUID();
}
});
@ -195,68 +195,60 @@ void AssignmentClientMonitor::checkSpares() {
SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId);
childNode->activateLocalSocket();
QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, childNode);
auto diePacket = NLPacket::create(PacketType::StopNode, 0);
nodeList->sendPacket(std::move(diePacket), *childNode);
}
}
}
void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<NLPacket> packet) {
// read out the sender ID
QUuid senderID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID));
void AssignmentClientMonitor::readPendingDatagrams() {
auto nodeList = DependencyManager::get<NodeList>();
QByteArray receivedPacket;
HifiSockAddr senderSockAddr;
SharedNodePointer matchingNode = nodeList->nodeWithUUID(senderID);
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
AssignmentClientChildData* childData = nullptr;
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
if (packetTypeForPacket(receivedPacket) == PacketTypeNodeJsonStats) {
QUuid packetUUID = uuidFromPacketHeader(receivedPacket);
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (!matchingNode) {
// The parent only expects to be talking with prorams running on this same machine.
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
if (!packetUUID.isNull()) {
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode
(packetUUID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false);
AssignmentClientChildData *childData = new AssignmentClientChildData("unknown");
matchingNode->setLinkedData(childData);
} else {
// tell unknown assignment-client child to exit.
qDebug() << "asking unknown child to exit.";
QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr);
}
}
}
if (matchingNode) {
// update our records about how to reach this child
matchingNode->setLocalSocket(senderSockAddr);
QVariantMap packetVariantMap =
JSONBreakableMarshal::fromStringBuffer(receivedPacket.mid(numBytesForPacketHeader(receivedPacket)));
QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(packetVariantMap);
// get child's assignment type out of the decoded json
QString childType = unpackedStatsJSON["assignment_type"].toString();
AssignmentClientChildData *childData =
static_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
childData->setChildType(childType);
// note when this child talked
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
}
if (!matchingNode) {
// The parent only expects to be talking with prorams running on this same machine.
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
if (!senderID.isNull()) {
// We don't have this node yet - we should add it
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode
(senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false);
childData = new AssignmentClientChildData(Assignment::Type::AllTypes);
matchingNode->setLinkedData(childData);
} else {
// have the NodeList attempt to handle it
nodeList->processNodeData(senderSockAddr, receivedPacket);
// tell unknown assignment-client child to exit.
qDebug() << "Asking unknown child at" << senderSockAddr << "to exit.";
auto diePacket = NLPacket::create(PacketType::StopNode, 0);
nodeList->sendPacket(std::move(diePacket), senderSockAddr);
return;
}
}
} else {
childData = dynamic_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
}
if (childData) {
// update our records about how to reach this child
matchingNode->setLocalSocket(senderSockAddr);
// get child's assignment type out of the packet
quint8 assignmentType;
packet->readPrimitive(&assignmentType);
childData->setChildType((Assignment::Type) assignmentType);
// note when this child talked
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
}
}

View file

@ -23,7 +23,6 @@
extern const char* NUM_FORKS_PARAMETER;
class AssignmentClientMonitor : public QObject {
Q_OBJECT
public:
@ -35,9 +34,9 @@ public:
void stopChildProcesses();
private slots:
void readPendingDatagrams();
void checkSpares();
void childProcessFinished();
void handleChildStatusPacket(QSharedPointer<NLPacket> packet);
public slots:
void aboutToQuit();

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include "Agent.h"
#include "AssignmentFactory.h"
@ -17,15 +17,13 @@
#include "avatars/AvatarMixer.h"
#include "entities/EntityServer.h"
ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) {
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
quint8 packedType;
packetStream >> packedType;
packet.peekPrimitive(&packedType);
Assignment::Type unpackedType = (Assignment::Type) packedType;
switch (unpackedType) {
case Assignment::AudioMixerType:
return new AudioMixer(packet);

View file

@ -16,7 +16,7 @@
class AssignmentFactory {
public:
static ThreadedAssignment* unpackAssignment(const QByteArray& packet);
static ThreadedAssignment* unpackAssignment(NLPacket& packet);
};
#endif // hifi_AssignmentFactory_h

View file

@ -45,14 +45,13 @@
#include <NodeList.h>
#include <Node.h>
#include <OctreeConstants.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include <StDev.h>
#include <UUID.h>
#include "AudioRingBuffer.h"
#include "AudioMixerClientData.h"
#include "AudioMixerDatagramProcessor.h"
#include "AvatarAudioStream.h"
#include "InjectedAudioStream.h"
@ -75,7 +74,7 @@ bool AudioMixer::shouldMute(float quietestFrame) {
return (quietestFrame > _noiseMutingThreshold);
}
AudioMixer::AudioMixer(const QByteArray& packet) :
AudioMixer::AudioMixer(NLPacket& packet) :
ThreadedAssignment(packet),
_trailingSleepRatio(1.0f),
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
@ -94,6 +93,18 @@ AudioMixer::AudioMixer(const QByteArray& packet) :
{
// constant defined in AudioMixer.h. However, we don't want to include this here
// we will soon find a better common home for these audio-related constants
// SOON
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
QSet<PacketType::Value> nodeAudioPackets {
PacketType::MicrophoneAudioNoEcho, PacketType::MicrophoneAudioWithEcho,
PacketType::InjectAudio, PacketType::SilentAudioFrame,
PacketType::AudioStreamStats
};
packetReceiver.registerListenerForTypes(nodeAudioPackets, this, "handleNodeAudioPacket");
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
}
const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f;
@ -463,8 +474,6 @@ int AudioMixer::prepareMixForListeningNode(Node* node) {
}
void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
static char clientEnvBuffer[MAX_PACKET_SIZE];
// Send stream properties
bool hasReverb = false;
float reverbTime, wetLevel;
@ -489,6 +498,7 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
break;
}
}
AudioMixerClientData* nodeData = static_cast<AudioMixerClientData*>(node->getLinkedData());
AvatarAudioStream* stream = nodeData->getAvatarAudioStream();
bool dataChanged = (stream->hasReverb() != hasReverb) ||
@ -509,57 +519,49 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
if (sendData) {
auto nodeList = DependencyManager::get<NodeList>();
int numBytesEnvPacketHeader = nodeList->populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment);
char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader;
unsigned char bitset = 0;
int packetSize = sizeof(bitset);
if (hasReverb) {
packetSize += sizeof(reverbTime) + sizeof(wetLevel);
}
auto envPacket = NLPacket::create(PacketType::AudioEnvironment, packetSize);
if (hasReverb) {
setAtBit(bitset, HAS_REVERB_BIT);
}
memcpy(envDataAt, &bitset, sizeof(unsigned char));
envDataAt += sizeof(unsigned char);
envPacket->writePrimitive(bitset);
if (hasReverb) {
memcpy(envDataAt, &reverbTime, sizeof(float));
envDataAt += sizeof(float);
memcpy(envDataAt, &wetLevel, sizeof(float));
envDataAt += sizeof(float);
envPacket->writePrimitive(reverbTime);
envPacket->writePrimitive(wetLevel);
}
nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node);
nodeList->sendPacket(std::move(envPacket), *node);
}
}
void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
void AudioMixer::handleNodeAudioPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
DependencyManager::get<NodeList>()->updateNodeWithDataFromPacket(packet, sendingNode);
}
void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
auto nodeList = DependencyManager::get<NodeList>();
if (sendingNode->getCanAdjustLocks()) {
auto newPacket = NLPacket::create(PacketType::MuteEnvironment, packet->getPayloadSize());
// Copy payload
newPacket->write(packet->getPayload(), packet->getPayloadSize());
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
// pull any new audio data from nodes off of the network stack
PacketType mixerPacketType = packetTypeForPacket(receivedPacket);
if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho
|| mixerPacketType == PacketTypeMicrophoneAudioWithEcho
|| mixerPacketType == PacketTypeInjectAudio
|| mixerPacketType == PacketTypeSilentAudioFrame
|| mixerPacketType == PacketTypeAudioStreamStats) {
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
} else if (mixerPacketType == PacketTypeMuteEnvironment) {
SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (sendingNode->getCanAdjustLocks()) {
QByteArray packet = receivedPacket;
nodeList->populatePacketHeader(packet, PacketTypeMuteEnvironment);
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
node->getLinkedData() && node != sendingNode) {
nodeList->writeDatagram(packet, packet.size(), node);
}
});
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
node->getLinkedData() && node != sendingNode) {
nodeList->sendPacket(std::move(newPacket), *node);
}
} else {
// let processNodeData handle it.
nodeList->processNodeData(senderSockAddr, receivedPacket);
}
});
}
}
@ -653,32 +655,6 @@ void AudioMixer::run() {
// we do not want this event loop to be the handler for UDP datagrams, so disconnect
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
// setup a QThread with us as parent that will house the AudioMixerDatagramProcessor
_datagramProcessingThread = new QThread(this);
_datagramProcessingThread->setObjectName("Datagram Processor Thread");
// create an AudioMixerDatagramProcessor and move it to that thread
AudioMixerDatagramProcessor* datagramProcessor = new AudioMixerDatagramProcessor(nodeList->getNodeSocket(), thread());
datagramProcessor->moveToThread(_datagramProcessingThread);
// remove the NodeList as the parent of the node socket
nodeList->getNodeSocket().setParent(NULL);
nodeList->getNodeSocket().moveToThread(_datagramProcessingThread);
// let the datagram processor handle readyRead from node socket
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead,
datagramProcessor, &AudioMixerDatagramProcessor::readPendingDatagrams);
// connect to the datagram processing thread signal that tells us we have to handle a packet
connect(datagramProcessor, &AudioMixerDatagramProcessor::packetRequiresProcessing, this, &AudioMixer::readPendingDatagram);
// delete the datagram processor and the associated thread when the QThread quits
connect(_datagramProcessingThread, &QThread::finished, datagramProcessor, &QObject::deleteLater);
connect(datagramProcessor, &QObject::destroyed, _datagramProcessingThread, &QThread::deleteLater);
// start the datagram processing thread
_datagramProcessingThread->start();
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [](Node* node) {
@ -712,8 +688,6 @@ void AudioMixer::run() {
QElapsedTimer timer;
timer.start();
char clientMixBuffer[MAX_PACKET_SIZE];
int usecToSleep = AudioConstants::NETWORK_FRAME_USECS;
const int TRAILING_AVERAGE_FRAMES = 100;
@ -791,8 +765,8 @@ void AudioMixer::run() {
// if the stream should be muted, send mute packet
if (nodeData->getAvatarAudioStream()
&& shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) {
QByteArray packet = nodeList->byteArrayWithPopulatedHeader(PacketTypeNoisyMute);
nodeList->writeDatagram(packet, node);
auto mutePacket = NLPacket::create(PacketType::NoisyMute, 0);
nodeList->sendPacket(std::move(mutePacket), *node);
}
if (node->getType() == NodeType::Agent && node->getActiveSocket()
@ -800,41 +774,37 @@ void AudioMixer::run() {
int streamsMixed = prepareMixForListeningNode(node.data());
char* mixDataAt;
std::unique_ptr<NLPacket> mixPacket;
if (streamsMixed > 0) {
// pack header
int numBytesMixPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio);
mixDataAt = clientMixBuffer + numBytesMixPacketHeader;
int mixPacketBytes = sizeof(quint16) + AudioConstants::NETWORK_FRAME_BYTES_STEREO;
mixPacket = NLPacket::create(PacketType::MixedAudio, mixPacketBytes);
// pack sequence number
quint16 sequence = nodeData->getOutgoingSequenceNumber();
memcpy(mixDataAt, &sequence, sizeof(quint16));
mixDataAt += sizeof(quint16);
mixPacket->writePrimitive(sequence);
// pack mixed audio samples
memcpy(mixDataAt, _mixSamples, AudioConstants::NETWORK_FRAME_BYTES_STEREO);
mixDataAt += AudioConstants::NETWORK_FRAME_BYTES_STEREO;
mixPacket->write(reinterpret_cast<char*>(_mixSamples),
AudioConstants::NETWORK_FRAME_BYTES_STEREO);
} else {
// pack header
int numBytesPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame);
mixDataAt = clientMixBuffer + numBytesPacketHeader;
int silentPacketBytes = sizeof(quint16) + sizeof(quint16);
mixPacket = NLPacket::create(PacketType::SilentAudioFrame, silentPacketBytes);
// pack sequence number
quint16 sequence = nodeData->getOutgoingSequenceNumber();
memcpy(mixDataAt, &sequence, sizeof(quint16));
mixDataAt += sizeof(quint16);
mixPacket->writePrimitive(sequence);
// pack number of silent audio samples
quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
memcpy(mixDataAt, &numSilentSamples, sizeof(quint16));
mixDataAt += sizeof(quint16);
mixPacket->writePrimitive(numSilentSamples);
}
// Send audio environment
sendAudioEnvironmentPacket(node);
// send mixed audio packet
nodeList->writeDatagram(clientMixBuffer, mixDataAt - clientMixBuffer, node);
nodeList->sendPacket(std::move(mixPacket), *node);
nodeData->incrementOutgoingMixedAudioSequenceNumber();
// send an audio stream stats packet if it's time

View file

@ -28,20 +28,21 @@ const int READ_DATAGRAMS_STATS_WINDOW_SECONDS = 30;
class AudioMixer : public ThreadedAssignment {
Q_OBJECT
public:
AudioMixer(const QByteArray& packet);
AudioMixer(NLPacket& packet);
void deleteLater() { qDebug() << "DELETE LATER CALLED?"; QObject::deleteLater(); }
public slots:
/// threaded run of assignment
void run();
void readPendingDatagrams() { }; // this will not be called since our datagram processing thread will handle
void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
void sendStatsPacket();
static const InboundAudioStream::Settings& getStreamSettings() { return _streamSettings; }
private slots:
void handleNodeAudioPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
private:
/// adds one stream to the mix for a listening node
int addStreamToMixForListeningNodeWithStream(AudioMixerClientData* listenerNodeData,

View file

@ -12,7 +12,7 @@
#include <QtCore/QDebug>
#include <QtCore/QJsonArray>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <UUID.h>
#include "InjectedAudioStream.h"
@ -49,59 +49,64 @@ AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const {
return NULL;
}
int AudioMixerClientData::parseData(const QByteArray& packet) {
PacketType packetType = packetTypeForPacket(packet);
if (packetType == PacketTypeAudioStreamStats) {
const char* dataAt = packet.data();
int AudioMixerClientData::parseData(NLPacket& packet) {
PacketType::Value packetType = packet.getType();
if (packetType == PacketType::AudioStreamStats) {
// skip over header, appendFlag, and num stats packed
dataAt += (numBytesForPacketHeader(packet) + sizeof(quint8) + sizeof(quint16));
packet.seek(sizeof(quint8) + sizeof(quint16));
// read the downstream audio stream stats
memcpy(&_downstreamAudioStreamStats, dataAt, sizeof(AudioStreamStats));
dataAt += sizeof(AudioStreamStats);
packet.readPrimitive(&_downstreamAudioStreamStats);
return dataAt - packet.data();
return packet.pos();
} else {
PositionalAudioStream* matchingStream = NULL;
if (packetType == PacketTypeMicrophoneAudioWithEcho
|| packetType == PacketTypeMicrophoneAudioNoEcho
|| packetType == PacketTypeSilentAudioFrame) {
if (packetType == PacketType::MicrophoneAudioWithEcho
|| packetType == PacketType::MicrophoneAudioNoEcho
|| packetType == PacketType::SilentAudioFrame) {
QUuid nullUUID = QUuid();
if (!_audioStreams.contains(nullUUID)) {
// we don't have a mic stream yet, so add it
// read the channel flag to see if our stream is stereo or not
const char* channelFlagAt = packet.constData() + numBytesForPacketHeader(packet) + sizeof(quint16);
quint8 channelFlag = *(reinterpret_cast<const quint8*>(channelFlagAt));
packet.seek(sizeof(quint16));
quint8 channelFlag;
packet.readPrimitive(&channelFlag);
bool isStereo = channelFlag == 1;
_audioStreams.insert(nullUUID, matchingStream = new AvatarAudioStream(isStereo, AudioMixer::getStreamSettings()));
} else {
matchingStream = _audioStreams.value(nullUUID);
}
} else if (packetType == PacketTypeInjectAudio) {
} else if (packetType == PacketType::InjectAudio) {
// this is injected audio
// grab the stream identifier for this injected audio
int bytesBeforeStreamIdentifier = numBytesForPacketHeader(packet) + sizeof(quint16);
QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(bytesBeforeStreamIdentifier, NUM_BYTES_RFC4122_UUID));
int bytesBeforeStereoIdentifier = bytesBeforeStreamIdentifier + NUM_BYTES_RFC4122_UUID;
packet.seek(sizeof(quint16));
QUuid streamIdentifier = QUuid::fromRfc4122(packet.read(NUM_BYTES_RFC4122_UUID));
bool isStereo;
QDataStream(packet.mid(bytesBeforeStereoIdentifier)) >> isStereo;
packet.readPrimitive(&isStereo);
if (!_audioStreams.contains(streamIdentifier)) {
// we don't have this injected stream yet, so add it
_audioStreams.insert(streamIdentifier, matchingStream = new InjectedAudioStream(streamIdentifier, isStereo, AudioMixer::getStreamSettings()));
_audioStreams.insert(streamIdentifier,
matchingStream = new InjectedAudioStream(streamIdentifier, isStereo, AudioMixer::getStreamSettings()));
} else {
matchingStream = _audioStreams.value(streamIdentifier);
}
}
// seek to the beginning of the packet so that the next reader is in the right spot
packet.seek(0);
return matchingStream->parseData(packet);
}
return 0;
@ -111,7 +116,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() {
QHash<QUuid, PositionalAudioStream*>::ConstIterator i;
for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) {
PositionalAudioStream* stream = i.value();
if (stream->popFrames(1, true) > 0) {
stream->updateLastPopOutputLoudnessAndTrailingLoudness();
}
@ -147,53 +152,47 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer&
// since audio stream stats packets are sent periodically, this is a good place to remove our dead injected streams.
removeDeadInjectedStreams();
char packet[MAX_PACKET_SIZE];
auto nodeList = DependencyManager::get<NodeList>();
// The append flag is a boolean value that will be packed right after the header. The first packet sent
// The append flag is a boolean value that will be packed right after the header. The first packet sent
// inside this method will have 0 for this flag, while every subsequent packet will have 1 for this flag.
// The sole purpose of this flag is so the client can clear its map of injected audio stream stats when
// it receives a packet with an appendFlag of 0. This prevents the buildup of dead audio stream stats in the client.
quint8 appendFlag = 0;
// pack header
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats);
char* headerEndAt = packet + numBytesPacketHeader;
// calculate how many stream stat structs we can fit in each packet
const int numStreamStatsRoomFor = (MAX_PACKET_SIZE - numBytesPacketHeader - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
// pack and send stream stats packets until all audio streams' stats are sent
int numStreamStatsRemaining = _audioStreams.size();
QHash<QUuid, PositionalAudioStream*>::ConstIterator audioStreamsIterator = _audioStreams.constBegin();
while (numStreamStatsRemaining > 0) {
auto statsPacket = NLPacket::create(PacketType::AudioStreamStats);
char* dataAt = headerEndAt;
// pack the append flag
memcpy(dataAt, &appendFlag, sizeof(quint8));
// pack the append flag in this packet
statsPacket->writePrimitive(appendFlag);
appendFlag = 1;
dataAt += sizeof(quint8);
int numStreamStatsRoomFor = (statsPacket->size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats);
// calculate and pack the number of stream stats to follow
quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor);
memcpy(dataAt, &numStreamStatsToPack, sizeof(quint16));
dataAt += sizeof(quint16);
statsPacket->writePrimitive(numStreamStatsToPack);
// pack the calculated number of stream stats
for (int i = 0; i < numStreamStatsToPack; i++) {
PositionalAudioStream* stream = audioStreamsIterator.value();
stream->perSecondCallbackForUpdatingStats();
AudioStreamStats streamStats = stream->getAudioStreamStats();
memcpy(dataAt, &streamStats, sizeof(AudioStreamStats));
dataAt += sizeof(AudioStreamStats);
statsPacket->writePrimitive(streamStats);
audioStreamsIterator++;
}
numStreamStatsRemaining -= numStreamStatsToPack;
// send the current packet
nodeList->writeDatagram(packet, dataAt - packet, destinationNode);
nodeList->sendPacket(std::move(statsPacket), *destinationNode);
}
}
@ -220,7 +219,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const {
result["downstream"] = downstreamStats;
AvatarAudioStream* avatarAudioStream = getAvatarAudioStream();
if (avatarAudioStream) {
QJsonObject upstreamStats;
@ -246,7 +245,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const {
} else {
result["upstream"] = "mic unknown";
}
QHash<QUuid, PositionalAudioStream*>::ConstIterator i;
QJsonArray injectorArray;
for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) {
@ -270,7 +269,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const {
upstreamStats["min_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMin);
upstreamStats["max_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMax);
upstreamStats["avg_gap_30s"] = formatUsecTime(streamStats._timeGapWindowAverage);
injectorArray.push_back(upstreamStats);
}
}
@ -304,7 +303,7 @@ void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamS
streamStats._desiredJitterBufferFrames,
streamStats._framesAvailableAverage,
streamStats._framesAvailable);
printf(" Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u\n",
streamStats._starveCount,
streamStats._consecutiveNotMixedCount,
@ -323,10 +322,10 @@ void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamS
}
PerListenerSourcePairData* AudioMixerClientData::getListenerSourcePairData(const QUuid& sourceUUID) {
PerListenerSourcePairData* AudioMixerClientData::getListenerSourcePairData(const QUuid& sourceUUID) {
if (!_listenerSourcePairData.contains(sourceUUID)) {
PerListenerSourcePairData* newData = new PerListenerSourcePairData();
_listenerSourcePairData[sourceUUID] = newData;
}
return _listenerSourcePairData[sourceUUID];
return _listenerSourcePairData[sourceUUID];
}

View file

@ -25,7 +25,7 @@
class PerListenerSourcePairData {
public:
PerListenerSourcePairData() {
PerListenerSourcePairData() {
_penumbraFilter.initialize(AudioConstants::SAMPLE_RATE, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO / 2);
};
AudioFilterHSF1s& getPenumbraFilter() { return _penumbraFilter; }
@ -42,7 +42,7 @@ public:
const QHash<QUuid, PositionalAudioStream*>& getAudioStreams() const { return _audioStreams; }
AvatarAudioStream* getAvatarAudioStream() const;
int parseData(const QByteArray& packet);
int parseData(NLPacket& packet);
void checkBuffersBeforeFrameSend();

View file

@ -1,47 +0,0 @@
//
// AudioMixerDatagramProcessor.cpp
// assignment-client/src
//
// Created by Stephen Birarda on 2014-08-14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <HifiSockAddr.h>
#include <NodeList.h>
#include "AudioMixerDatagramProcessor.h"
AudioMixerDatagramProcessor::AudioMixerDatagramProcessor(QUdpSocket& nodeSocket, QThread* previousNodeSocketThread) :
_nodeSocket(nodeSocket),
_previousNodeSocketThread(previousNodeSocketThread)
{
}
AudioMixerDatagramProcessor::~AudioMixerDatagramProcessor() {
// return the node socket to its previous thread
_nodeSocket.moveToThread(_previousNodeSocketThread);
}
void AudioMixerDatagramProcessor::readPendingDatagrams() {
HifiSockAddr senderSockAddr;
static QByteArray incomingPacket;
// read everything that is available
while (_nodeSocket.hasPendingDatagrams()) {
incomingPacket.resize(_nodeSocket.pendingDatagramSize());
// just get this packet off the stack
_nodeSocket.readDatagram(incomingPacket.data(), incomingPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
// emit the signal to tell AudioMixer it needs to process a packet
emit packetRequiresProcessing(incomingPacket, senderSockAddr);
}
}

View file

@ -1,32 +0,0 @@
//
// AudioMixerDatagramProcessor.h
// assignment-client/src
//
// Created by Stephen Birarda on 2014-08-14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AudioMixerDatagramProcessor_h
#define hifi_AudioMixerDatagramProcessor_h
#include <qobject.h>
#include <qudpsocket.h>
class AudioMixerDatagramProcessor : public QObject {
Q_OBJECT
public:
AudioMixerDatagramProcessor(QUdpSocket& nodeSocket, QThread* previousNodeSocketThread);
~AudioMixerDatagramProcessor();
public slots:
void readPendingDatagrams();
signals:
void packetRequiresProcessing(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
private:
QUdpSocket& _nodeSocket;
QThread* _previousNodeSocketThread;
};
#endif // hifi_AudioMixerDatagramProcessor_h

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include "AvatarAudioStream.h"
@ -18,10 +18,10 @@ AvatarAudioStream::AvatarAudioStream(bool isStereo, const InboundAudioStream::Se
{
}
int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
int AvatarAudioStream::parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
int readBytes = 0;
if (type == PacketTypeSilentAudioFrame) {
if (type == PacketType::SilentAudioFrame) {
const char* dataAt = packetAfterSeqNum.constData();
quint16 numSilentSamples = *(reinterpret_cast<const quint16*>(dataAt));
readBytes += sizeof(quint16);
@ -31,7 +31,7 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray&
readBytes += parsePositionalData(packetAfterSeqNum.mid(readBytes));
} else {
_shouldLoopbackForNode = (type == PacketTypeMicrophoneAudioWithEcho);
_shouldLoopbackForNode = (type == PacketType::MicrophoneAudioWithEcho);
// read the channel flag
quint8 channelFlag = packetAfterSeqNum.at(readBytes);
@ -48,11 +48,11 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray&
// read the positional data
readBytes += parsePositionalData(packetAfterSeqNum.mid(readBytes));
// calculate how many samples are in this packet
int numAudioBytes = packetAfterSeqNum.size() - readBytes;
numAudioSamples = numAudioBytes / sizeof(int16_t);
}
return readBytes;
}

View file

@ -19,13 +19,13 @@
class AvatarAudioStream : public PositionalAudioStream {
public:
AvatarAudioStream(bool isStereo, const InboundAudioStream::Settings& settings);
private:
// disallow copying of AvatarAudioStream objects
AvatarAudioStream(const AvatarAudioStream&);
AvatarAudioStream& operator= (const AvatarAudioStream&);
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
};
#endif // hifi_AvatarAudioStream_h

View file

@ -20,7 +20,7 @@
#include <LogHandler.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include <UUID.h>
#include <TryLocker.h>
@ -30,10 +30,10 @@
const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60;
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60;
const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000;
AvatarMixer::AvatarMixer(const QByteArray& packet) :
AvatarMixer::AvatarMixer(NLPacket& packet) :
ThreadedAssignment(packet),
_broadcastThread(),
_lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()),
@ -46,6 +46,12 @@ AvatarMixer::AvatarMixer(const QByteArray& packet) :
{
// make sure we hear about node kills so we can tell the other nodes
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AvatarData, this, "handleAvatarDataPacket");
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "handleAvatarBillboardPacket");
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
}
AvatarMixer::~AvatarMixer() {
@ -63,73 +69,70 @@ const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f;
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
// if the avatar is not in view or in the keyhole.
void AvatarMixer::broadcastAvatarData() {
int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp;
++_numStatFrames;
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
const float RATIO_BACK_OFF = 0.02f;
const int TRAILING_AVERAGE_FRAMES = 100;
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
// NOTE: The following code calculates the _performanceThrottlingRatio based on how much the avatar-mixer was
// able to sleep. This will eventually be used to ask for an additional avatar-mixer to help out. Currently the value
// is unused as it is assumed this should not be hit before the avatar-mixer hits the desired bandwidth limit per client.
// It is reported in the domain-server stats for the avatar-mixer.
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
+ (idleTime * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_MSECS);
float lastCutoffRatio = _performanceThrottlingRatio;
bool hasRatioChanged = false;
if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
// we're struggling - change our performance throttling ratio
_performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio));
qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
hasRatioChanged = true;
} else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) {
// we've recovered and can back off the performance throttling
_performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF;
if (_performanceThrottlingRatio < 0) {
_performanceThrottlingRatio = 0;
}
qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
<< lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
hasRatioChanged = true;
}
if (hasRatioChanged) {
framesSinceCutoffEvent = 0;
}
}
if (!hasRatioChanged) {
++framesSinceCutoffEvent;
}
static QByteArray mixedAvatarByteArray;
auto nodeList = DependencyManager::get<NodeList>();
int numPacketHeaderBytes = nodeList->populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);
// setup for distributed random floating point values
// setup for distributed random floating point values
std::random_device randomDevice;
std::mt19937 generator(randomDevice());
std::uniform_real_distribution<float> distribution;
nodeList->eachMatchingNode(
[&](const SharedNodePointer& node)->bool {
if (!node->getLinkedData()) {
@ -150,25 +153,22 @@ void AvatarMixer::broadcastAvatarData() {
return;
}
++_sumListeners;
// reset packet pointers for this node
mixedAvatarByteArray.resize(numPacketHeaderBytes);
AvatarData& avatar = nodeData->getAvatar();
glm::vec3 myPosition = avatar.getPosition();
// reset the internal state for correct random number distribution
distribution.reset();
// reset the max distance for this frame
float maxAvatarDistanceThisFrame = 0.0f;
// reset the number of sent avatars
nodeData->resetNumAvatarsSentLastFrame();
// keep a counter of the number of considered avatars
int numOtherAvatars = 0;
// keep track of outbound data rate specifically for avatar data
int numAvatarDataBytes = 0;
@ -185,7 +185,7 @@ void AvatarMixer::broadcastAvatarData() {
// bandwidth to this node. We do this once a second, which is also the window for
// the bandwidth reported by node->getOutboundBandwidth();
if (nodeData->getNumFramesSinceFRDAdjustment() > AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) {
const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f;
const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO);
const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f));
@ -195,22 +195,22 @@ void AvatarMixer::broadcastAvatarData() {
if (avatarDataRateLastSecond > _maxKbpsPerNode) {
// is the FRD greater than the farthest avatar?
// is the FRD greater than the farthest avatar?
// if so, before we calculate anything, set it to that distance
currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance());
// we're adjusting the full rate distance to target a bandwidth in the middle
// of the hysterisis gap
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
nodeData->setFullRateDistance(currentFullRateDistance);
nodeData->resetNumFramesSinceFRDAdjustment();
} else if (currentFullRateDistance < nodeData->getMaxAvatarDistance()
} else if (currentFullRateDistance < nodeData->getMaxAvatarDistance()
&& avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) {
// we are constrained AND we've recovered to below the acceptable ratio
// we are constrained AND we've recovered to below the acceptable ratio
// lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap
currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond;
nodeData->setFullRateDistance(currentFullRateDistance);
nodeData->resetNumFramesSinceFRDAdjustment();
}
@ -218,6 +218,9 @@ void AvatarMixer::broadcastAvatarData() {
nodeData->incrementNumFramesSinceFRDAdjustment();
}
// setup a PacketList for the avatarPackets
NLPacketList avatarPacketList(PacketType::BulkAvatarData);
// this is an AGENT we have received head data from
// send back a packet with other active node data to this node
nodeList->eachMatchingNode(
@ -233,16 +236,16 @@ void AvatarMixer::broadcastAvatarData() {
},
[&](const SharedNodePointer& otherNode) {
++numOtherAvatars;
AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
MutexTryLocker lock(otherNodeData->getMutex());
if (!lock.isLocked()) {
return;
}
AvatarData& otherAvatar = otherNodeData->getAvatar();
// Decide whether to send this avatar's data based on it's distance from us
// The full rate distance is the distance at which EVERY update will be sent for this avatar
// at twice the full rate distance, there will be a 50% chance of sending this avatar's update
glm::vec3 otherPosition = otherAvatar.getPosition();
@ -251,24 +254,24 @@ void AvatarMixer::broadcastAvatarData() {
// potentially update the max full rate distance for this frame
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
if (distanceToAvatar != 0.0f
if (distanceToAvatar != 0.0f
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
return;
}
PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData);
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData);
if (lastSeqToReceiver > lastSeqFromSender) {
// Did we somehow get out of order packets from the sender?
// We don't expect this to happen - in RELEASE we add this to a trackable stat
// and in DEBUG we crash on the assert
otherNodeData->incrementNumOutOfOrderSends();
assert(false);
}
// make sure we haven't already sent this data from this sender to this receiver
// or that somehow we haven't sent
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
@ -277,76 +280,77 @@ void AvatarMixer::broadcastAvatarData() {
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
++numAvatarsWithSkippedFrames;
}
}
// we're going to send this avatar
// increment the number of avatars sent to this reciever
nodeData->incrementNumAvatarsSentLastFrame();
// set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData));
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData));
QByteArray avatarByteArray;
avatarByteArray.append(otherNode->getUUID().toRfc4122());
avatarByteArray.append(otherAvatar.toByteArray());
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
nodeList->writeDatagram(mixedAvatarByteArray, node);
// start a new segment in the PacketList for this avatar
avatarPacketList.startSegment();
numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122());
numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray());
avatarPacketList.endSegment();
numAvatarDataBytes += mixedAvatarByteArray.size();
// reset the packet
mixedAvatarByteArray.resize(numPacketHeaderBytes);
}
// copy the avatar into the mixedAvatarByteArray packet
mixedAvatarByteArray.append(avatarByteArray);
// if the receiving avatar has just connected make sure we send out the mesh and billboard
// for this avatar (assuming they exist)
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
// we will also force a send of billboard or identity packet
// if either has changed in the last frame
if (otherNodeData->getBillboardChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
billboardPacket.append(otherNode->getUUID().toRfc4122());
billboardPacket.append(otherNodeData->getAvatar().getBillboard());
nodeList->writeDatagram(billboardPacket, node);
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
billboardPacket->write(rfcUUID);
billboardPacket->write(billboard);
nodeList->sendPacket(std::move(billboardPacket), *node);
++_sumBillboardPackets;
}
if (otherNodeData->getIdentityChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
identityPacket.append(individualData);
nodeList->writeDatagram(identityPacket, node);
identityPacket->write(individualData);
nodeList->sendPacket(std::move(identityPacket), *node);
++_sumIdentityPackets;
}
});
// send the last packet
nodeList->writeDatagram(mixedAvatarByteArray, node);
// close the current packet so that we're always sending something
avatarPacketList.closeCurrentPacket(true);
// send the avatar data PacketList
nodeList->sendPacketList(avatarPacketList, *node);
// record the bytes sent for other avatar data in the AvatarMixerClientData
nodeData->recordSentAvatarData(numAvatarDataBytes + mixedAvatarByteArray.size());
nodeData->recordSentAvatarData(numAvatarDataBytes);
// record the number of avatars held back this frame
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
@ -359,7 +363,7 @@ void AvatarMixer::broadcastAvatarData() {
}
}
);
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
}
@ -370,10 +374,10 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
// this was an avatar we were sending to other people
// send a kill packet for it to our other nodes
QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
killPacket += killedNode->getUUID().toRfc4122();
nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::Agent);
auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID);
killPacket->write(killedNode->getUUID().toRfc4122());
nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent);
// we also want to remove sequence number data for this avatar on our other avatars
// so invoke the appropriate method on the AvatarMixerClientData for other avatars
@ -399,79 +403,56 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
}
}
void AvatarMixer::readPendingDatagrams() {
QByteArray receivedPacket;
HifiSockAddr senderSockAddr;
void AvatarMixer::handleAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
auto nodeList = DependencyManager::get<NodeList>();
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
switch (packetTypeForPacket(receivedPacket)) {
case PacketTypeAvatarData: {
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
break;
}
case PacketTypeAvatarIdentity: {
// check if we have a matching node in our list
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
if (avatarNode && avatarNode->getLinkedData()) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
AvatarData& avatar = nodeData->getAvatar();
// parse the identity packet and update the change timestamp if appropriate
if (avatar.hasIdentityChangedAfterParsing(receivedPacket)) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
}
}
break;
}
case PacketTypeAvatarBillboard: {
// check if we have a matching node in our list
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
if (avatarNode && avatarNode->getLinkedData()) {
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
AvatarData& avatar = nodeData->getAvatar();
// parse the billboard packet and update the change timestamp if appropriate
if (avatar.hasBillboardChangedAfterParsing(receivedPacket)) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
}
}
break;
}
case PacketTypeKillAvatar: {
nodeList->processKillNode(receivedPacket);
break;
}
default:
// hand this off to the NodeList
nodeList->processNodeData(senderSockAddr, receivedPacket);
break;
nodeList->updateNodeWithDataFromPacket(packet, senderNode);
}
void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
if (senderNode->getLinkedData()) {
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
if (nodeData != nullptr) {
AvatarData& avatar = nodeData->getAvatar();
// parse the identity packet and update the change timestamp if appropriate
if (avatar.hasIdentityChangedAfterParsing(*packet)) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
}
}
}
}
void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
if (nodeData) {
AvatarData& avatar = nodeData->getAvatar();
// parse the billboard packet and update the change timestamp if appropriate
if (avatar.hasBillboardChangedAfterParsing(*packet)) {
QMutexLocker nodeDataLocker(&nodeData->getMutex());
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
}
}
}
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<NLPacket> packet) {
DependencyManager::get<NodeList>()->processKillNode(*packet);
}
void AvatarMixer::sendStatsPacket() {
QJsonObject statsObject;
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames;
statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames;
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio;
QJsonObject avatarsObject;
auto nodeList = DependencyManager::get<NodeList>();
// add stats for each listerner
nodeList->eachNode([&](const SharedNodePointer& node) {
@ -482,7 +463,7 @@ void AvatarMixer::sendStatsPacket() {
// add the key to ask the domain-server for a username replacement, if it has it
avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth();
AvatarMixerClientData* clientData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
if (clientData) {
MutexTryLocker lock(clientData->getMutex());
@ -490,9 +471,9 @@ void AvatarMixer::sendStatsPacket() {
clientData->loadJSONStats(avatarStats);
// add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only
avatarStats["delta_full_vs_avatar_data_kbps"] =
avatarStats["delta_full_vs_avatar_data_kbps"] =
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble();
}
}
}
avatarsObject[uuidStringWithoutCurlyBraces(node->getUUID())] = avatarStats;
@ -500,7 +481,7 @@ void AvatarMixer::sendStatsPacket() {
statsObject["avatars"] = avatarsObject;
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
_sumListeners = 0;
_sumBillboardPackets = 0;
_sumIdentityPackets = 0;
@ -509,41 +490,41 @@ void AvatarMixer::sendStatsPacket() {
void AvatarMixer::run() {
ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
auto nodeList = DependencyManager::get<NodeList>();
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [] (Node* node) {
node->setLinkedData(new AvatarMixerClientData());
};
// setup the timer that will be fired on the broadcast thread
_broadcastTimer = new QTimer;
_broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS);
_broadcastTimer->moveToThread(&_broadcastThread);
// connect appropriate signals and slots
connect(_broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection);
connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start()));
// wait until we have the domain-server settings, otherwise we bail
DomainHandler& domainHandler = nodeList->getDomainHandler();
qDebug() << "Waiting for domain settings from domain-server.";
// block until we get the settingsRequestComplete signal
QEventLoop loop;
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
domainHandler.requestDomainSettings();
loop.exec();
if (domainHandler.getSettingsObject().isEmpty()) {
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
setFinished(true);
return;
}
// parse the settings to pull out the values we need
parseDomainServerSettings(domainHandler.getSettingsObject());
@ -554,13 +535,13 @@ void AvatarMixer::run() {
void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
const QString AVATAR_MIXER_SETTINGS_KEY = "avatar_mixer";
const QString NODE_SEND_BANDWIDTH_KEY = "max_node_send_bandwidth";
const float DEFAULT_NODE_SEND_BANDWIDTH = 1.0f;
QJsonValue nodeBandwidthValue = domainSettings[AVATAR_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY];
if (!nodeBandwidthValue.isDouble()) {
qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value";
}
}
_maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA;
qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps.";
qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps.";
}

View file

@ -19,19 +19,23 @@
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
class AvatarMixer : public ThreadedAssignment {
Q_OBJECT
public:
AvatarMixer(const QByteArray& packet);
AvatarMixer(NLPacket& packet);
~AvatarMixer();
public slots:
/// runs the avatar mixer
void run();
void nodeAdded(SharedNodePointer nodeAdded);
void nodeKilled(SharedNodePointer killedNode);
void readPendingDatagrams();
void sendStatsPacket();
private slots:
void handleAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleKillAvatarPacket(QSharedPointer<NLPacket> packet);
private:
void broadcastAvatarData();

View file

@ -9,14 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include "AvatarMixerClientData.h"
int AvatarMixerClientData::parseData(const QByteArray& packet) {
int AvatarMixerClientData::parseData(NLPacket& packet) {
// compute the offset to the data payload
int offset = numBytesForPacketHeader(packet);
return _avatar.parseDataAtOffset(packet, offset);
return _avatar.parseDataFromBuffer(packet.read(packet.bytesLeftToRead()));
}
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {

View file

@ -22,7 +22,7 @@
#include <AvatarData.h>
#include <NodeData.h>
#include <NumericalConstants.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <SimpleMovingAverage.h>
#include <UUIDHasher.h>
@ -31,13 +31,13 @@ const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
class AvatarMixerClientData : public NodeData {
Q_OBJECT
public:
int parseData(const QByteArray& packet);
int parseData(NLPacket& packet);
AvatarData& getAvatar() { return _avatar; }
bool checkAndSetHasReceivedFirstPackets();
PacketSequenceNumber getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber)
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber)
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
@ -57,7 +57,7 @@ public:
void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; }
int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; }
void recordNumOtherAvatarStarves(int numAvatarsHeldBack) { _otherAvatarStarves.updateAverage((float) numAvatarsHeldBack); }
void recordNumOtherAvatarStarves(int numAvatarsHeldBack) { _otherAvatarStarves.updateAverage((float) numAvatarsHeldBack); }
float getAvgNumOtherAvatarStarvesPerSecond() const { return _otherAvatarStarves.getAverageSampleValuePerSecond(); }
void recordNumOtherAvatarSkips(int numOtherAvatarSkips) { _otherAvatarSkips.updateAverage((float) numOtherAvatarSkips); }
@ -71,7 +71,7 @@ public:
void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); }
float getOutboundAvatarDataKbps() const
float getOutboundAvatarDataKbps() const
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
void loadJSONStats(QJsonObject& jsonObject) const;

View file

@ -12,7 +12,7 @@
#ifndef hifi_EntityNodeData_h
#define hifi_EntityNodeData_h
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include "../octree/OctreeQueryNode.h"
@ -22,7 +22,7 @@ public:
OctreeQueryNode(),
_lastDeletedEntitiesSentAt(0) { }
virtual PacketType getMyPacketType() const { return PacketTypeEntityData; }
virtual PacketType::Value getMyPacketType() const { return PacketType::EntityData; }
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }

View file

@ -21,9 +21,13 @@ const char* MODEL_SERVER_NAME = "Entity";
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
EntityServer::EntityServer(const QByteArray& packet)
: OctreeServer(packet), _entitySimulation(NULL) {
// nothing special to do here...
EntityServer::EntityServer(NLPacket& packet) :
OctreeServer(packet),
_entitySimulation(NULL)
{
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase },
this, "handleEntityPacket");
}
EntityServer::~EntityServer() {
@ -36,6 +40,12 @@ EntityServer::~EntityServer() {
tree->removeNewlyCreatedHook(this);
}
void EntityServer::handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->queueReceivedPacket(packet, senderNode);
}
}
OctreeQueryNode* EntityServer::createOctreeQueryNode() {
return new EntityNodeData();
}
@ -64,7 +74,7 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo
// EntityServer will use the "special packets" to send list of recently deleted entities
bool EntityServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) {
bool shouldSendDeletedEntities = false;
// check to see if any new entities have been added since we last sent to this node...
@ -79,9 +89,8 @@ bool EntityServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
return shouldSendDeletedEntities;
}
int EntityServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
unsigned char outputBuffer[MAX_PACKET_SIZE];
size_t packetLength = 0;
int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
int totalBytes = 0;
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
if (nodeData) {
@ -91,23 +100,25 @@ int EntityServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNo
EntityTree* tree = static_cast<EntityTree*>(_tree);
bool hasMoreToSend = true;
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 entities?
packetsSent = 0;
while (hasMoreToSend) {
hasMoreToSend = tree->encodeEntitiesDeletedSince(queryNode->getSequenceNumber(), deletedEntitiesSentAt,
outputBuffer, MAX_PACKET_SIZE, packetLength);
DependencyManager::get<NodeList>()->writeDatagram((char*) outputBuffer, packetLength,
SharedNodePointer(node));
queryNode->packetSent(outputBuffer, packetLength);
while (hasMoreToSend) {
auto specialPacket = tree->encodeEntitiesDeletedSince(queryNode->getSequenceNumber(), deletedEntitiesSentAt,
hasMoreToSend);
queryNode->packetSent(*specialPacket);
totalBytes += specialPacket->getDataSize();
packetsSent++;
DependencyManager::get<NodeList>()->sendPacket(std::move(specialPacket), *node);
}
nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt);
}
// TODO: caller is expecting a packetLength, what if we send more than one packet??
return packetLength;
return totalBytes;
}
void EntityServer::pruneDeletedEntities() {
@ -115,7 +126,7 @@ void EntityServer::pruneDeletedEntities() {
if (tree->hasAnyDeletedEntities()) {
quint64 earliestLastDeletedEntitiesSent = usecTimestampNow() + 1; // in the future
DependencyManager::get<NodeList>()->eachNode([&earliestLastDeletedEntitiesSent](const SharedNodePointer& node) {
if (node->getLinkedData()) {
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
@ -125,12 +136,12 @@ void EntityServer::pruneDeletedEntities() {
}
}
});
tree->forgetEntitiesDeletedBefore(earliestLastDeletedEntitiesSent);
}
}
void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectionObject) {
void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectionObject) {
bool wantEditLogging = false;
readOptionBool(QString("wantEditLogging"), settingsSectionObject, wantEditLogging);
qDebug("wantEditLogging=%s", debug::valueOf(wantEditLogging));

View file

@ -22,23 +22,23 @@
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
Q_OBJECT
public:
EntityServer(const QByteArray& packet);
EntityServer(NLPacket& packet);
~EntityServer();
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode();
virtual char getMyNodeType() const { return NodeType::EntityServer; }
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
virtual PacketType getMyEditNackType() const { return PacketTypeEntityEditNack; }
virtual PacketType::Value getMyEditNackType() const { return PacketType::EntityEditNack; }
virtual QString getMyDomainSettingsKey() const { return QString("entity_server_settings"); }
// subclass may implement these method
virtual void beforeRun();
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node);
virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode);
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject);
@ -49,6 +49,9 @@ public slots:
protected:
virtual Octree* createTree();
private slots:
void handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
private:
EntitySimulation* _entitySimulation;
QTimer* _pruneDeletedEntitiesTimer = nullptr;

View file

@ -12,7 +12,7 @@
#include <limits>
#include <NumericalConstants.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <PerfStat.h>
#include "OctreeServer.h"
@ -74,7 +74,7 @@ void OctreeInboundPacketProcessor::midProcess() {
}
}
void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
if (_shuttingDown) {
qDebug() << "OctreeInboundPacketProcessor::processPacket() while shutting down... ignoring incoming packet";
return;
@ -83,22 +83,24 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin
bool debugProcessPacket = _myServer->wantsVerboseDebug();
if (debugProcessPacket) {
qDebug("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%d", &packet, packet.size());
qDebug("OctreeInboundPacketProcessor::processPacket() payload=%p payloadLength=%lld",
packet->getPayload(),
packet->getPayloadSize());
}
int numBytesPacketHeader = numBytesForPacketHeader(packet);
// Ask our tree subclass if it can handle the incoming packet...
PacketType packetType = packetTypeForPacket(packet);
PacketType::Value packetType = packet->getType();
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE",debugProcessPacket);
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
_receivedPacketCount++;
const unsigned char* packetData = reinterpret_cast<const unsigned char*>(packet.data());
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
quint64 sentAt = (*((quint64*)(packetData + numBytesPacketHeader + sizeof(sequence))));
unsigned short int sequence;
packet->readPrimitive(&sequence);
quint64 sentAt;
packet->readPrimitive(&sentAt);
quint64 arrivedAt = usecTimestampNow();
if (sentAt > arrivedAt) {
if (debugProcessPacket || _myServer->wantsDebugReceiving()) {
@ -107,6 +109,7 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin
}
sentAt = arrivedAt;
}
quint64 transitTime = arrivedAt - sentAt;
int editsInPacket = 0;
quint64 processTime = 0;
@ -114,53 +117,53 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin
if (debugProcessPacket || _myServer->wantsDebugReceiving()) {
qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount << " command from client";
qDebug() << " receivedBytes=" << packet.size();
qDebug() << " receivedBytes=" << packet->getDataSize();
qDebug() << " sequence=" << sequence;
qDebug() << " sentAt=" << sentAt << " usecs";
qDebug() << " arrivedAt=" << arrivedAt << " usecs";
qDebug() << " transitTime=" << transitTime << " usecs";
qDebug() << " sendingNode->getClockSkewUsec()=" << sendingNode->getClockSkewUsec() << " usecs";
}
if (debugProcessPacket) {
qDebug() << " numBytesPacketHeader=" << numBytesPacketHeader;
qDebug() << " numBytesPacketHeader=" << packet->totalHeadersSize();
qDebug() << " sizeof(sequence)=" << sizeof(sequence);
qDebug() << " sizeof(sentAt)=" << sizeof(sentAt);
}
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
if (debugProcessPacket) {
qDebug() << " atByte=" << atByte;
qDebug() << " packet.size()=" << packet.size();
if (atByte >= packet.size()) {
qDebug() << " atByte (in payload)=" << packet->pos();
qDebug() << " payload size=" << packet->getPayloadSize();
if (!packet->bytesLeftToRead()) {
qDebug() << " ----- UNEXPECTED ---- got a packet without any edit details!!!! --------";
}
}
unsigned char* editData = (unsigned char*)&packetData[atByte];
while (atByte < packet.size()) {
const unsigned char* editData = nullptr;
int maxSize = packet.size() - atByte;
while (packet->bytesLeftToRead() > 0) {
editData = reinterpret_cast<const unsigned char*>(packet->getPayload() + packet->pos());
int maxSize = packet->bytesLeftToRead();
if (debugProcessPacket) {
qDebug() << " --- inside while loop ---";
qDebug() << " maxSize=" << maxSize;
qDebug("OctreeInboundPacketProcessor::processPacket() %c "
"packetData=%p packetLength=%d editData=%p atByte=%d maxSize=%d",
packetType, packetData, packet.size(), editData, atByte, maxSize);
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
packetType, packet->getPayload(), packet->getPayloadSize(), editData,
packet->pos(), maxSize);
}
quint64 startLock = usecTimestampNow();
_myServer->getOctree()->lockForWrite();
quint64 startProcess = usecTimestampNow();
int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType,
reinterpret_cast<const unsigned char*>(packet.data()),
packet.size(),
editData, maxSize, sendingNode);
int editDataBytesRead =
_myServer->getOctree()->processEditPacketData(*packet, editData, maxSize, sendingNode);
if (debugProcessPacket) {
qDebug() << "OctreeInboundPacketProcessor::processPacket() after processEditPacketData()..."
@ -177,27 +180,25 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin
lockWaitTime += thisLockWaitTime;
// skip to next edit record in the packet
editData += editDataBytesRead;
atByte += editDataBytesRead;
packet->seek(packet->pos() + editDataBytesRead);
if (debugProcessPacket) {
qDebug() << " editDataBytesRead=" << editDataBytesRead;
qDebug() << " AFTER processEditPacketData atByte=" << atByte;
qDebug() << " AFTER processEditPacketData packet.size()=" << packet.size();
qDebug() << " AFTER processEditPacketData payload position=" << packet->pos();
qDebug() << " AFTER processEditPacketData payload size=" << packet->getPayloadSize();
}
}
if (debugProcessPacket) {
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c "
"packetData=%p packetLength=%d editData=%p atByte=%d",
packetType, packetData, packet.size(), editData, atByte);
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
packetType, packet->getPayload(), packet->getPayloadSize(), editData, packet->pos());
}
// Make sure our Node and NodeList knows we've heard from this node.
QUuid& nodeUUID = DEFAULT_NODE_ID_REF;
if (sendingNode) {
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
nodeUUID = sendingNode->getUUID();
if (debugProcessPacket) {
qDebug() << "sender has uuid=" << nodeUUID;
@ -235,21 +236,21 @@ void OctreeInboundPacketProcessor::trackInboundPacket(const QUuid& nodeUUID, uns
}
int OctreeInboundPacketProcessor::sendNackPackets() {
int packetsSent = 0;
if (_shuttingDown) {
qDebug() << "OctreeInboundPacketProcessor::sendNackPackets() while shutting down... ignore";
return packetsSent;
return 0;
}
char packet[MAX_PACKET_SIZE];
NLPacketList nackPacketList(_myServer->getMyEditNackType());
auto nodeList = DependencyManager::get<NodeList>();
int packetsSent = 0;
NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
while (i != _singleSenderStats.end()) {
QUuid nodeUUID = i.key();
SingleSenderStats nodeStats = i.value();
// check if this node is still alive. Remove its stats if it's dead.
if (!isAlive(nodeUUID)) {
i = _singleSenderStats.erase(i);
@ -259,7 +260,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
// if there are packets from _node that are waiting to be processed,
// don't send a NACK since the missing packets may be among those waiting packets.
if (hasPacketsToProcessFrom(nodeUUID)) {
i++;
++i;
continue;
}
@ -268,48 +269,31 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
// retrieve sequence number stats of node, prune its missing set
SequenceNumberStats& sequenceNumberStats = nodeStats.getIncomingEditSequenceNumberStats();
sequenceNumberStats.pruneMissingSet();
// construct nack packet(s) for this node
const QSet<unsigned short int>& missingSequenceNumbers = sequenceNumberStats.getMissingSet();
int numSequenceNumbersAvailable = missingSequenceNumbers.size();
QSet<unsigned short int>::const_iterator missingSequenceNumberIterator = missingSequenceNumbers.constBegin();
while (numSequenceNumbersAvailable > 0) {
char* dataAt = packet;
int bytesRemaining = MAX_PACKET_SIZE;
auto nodeList = DependencyManager::get<NodeList>();
auto it = missingSequenceNumbers.constBegin();
// pack header
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, _myServer->getMyEditNackType());
dataAt += numBytesPacketHeader;
bytesRemaining -= numBytesPacketHeader;
// calculate and pack the number of sequence numbers to nack
int numSequenceNumbersRoomFor = (bytesRemaining - sizeof(uint16_t)) / sizeof(unsigned short int);
uint16_t numSequenceNumbers = std::min(numSequenceNumbersAvailable, numSequenceNumbersRoomFor);
uint16_t* numSequenceNumbersAt = (uint16_t*)dataAt;
*numSequenceNumbersAt = numSequenceNumbers;
dataAt += sizeof(uint16_t);
// pack sequence numbers to nack
for (uint16_t i = 0; i < numSequenceNumbers; i++) {
unsigned short int* sequenceNumberAt = (unsigned short int*)dataAt;
*sequenceNumberAt = *missingSequenceNumberIterator;
dataAt += sizeof(unsigned short int);
missingSequenceNumberIterator++;
}
numSequenceNumbersAvailable -= numSequenceNumbers;
// send it
nodeList->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode);
packetsSent++;
qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID;
while (it != missingSequenceNumbers.constEnd()) {
unsigned short int sequenceNumber = *it;
nackPacketList.writePrimitive(sequenceNumber);
++it;
}
i++;
if (nackPacketList.getNumPackets()) {
qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID;
packetsSent += nackPacketList.getNumPackets();
// send the list of nack packets
nodeList->sendPacketList(nackPacketList, *destinationNode);
}
++i;
}
return packetsSent;
}

View file

@ -29,9 +29,9 @@ public:
quint64 getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
quint64 getTotalElementsProcessed() const { return _totalElementsInPacket; }
quint64 getTotalPacketsProcessed() const { return _totalPackets; }
quint64 getAverageProcessTimePerElement() const
quint64 getAverageProcessTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
quint64 getAverageLockWaitTimePerElement() const
quint64 getAverageLockWaitTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
const SequenceNumberStats& getIncomingEditSequenceNumberStats() const { return _incomingEditSequenceNumberStats; }
@ -40,7 +40,7 @@ public:
void trackInboundPacket(unsigned short int incomingSequence, quint64 transitTime,
int editsInPacket, quint64 processTime, quint64 lockWaitTime);
quint64 _totalTransitTime;
quint64 _totalTransitTime;
quint64 _totalProcessTime;
quint64 _totalLockWaitTime;
quint64 _totalElementsInPacket;
@ -53,7 +53,7 @@ typedef QHash<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterator;
typedef QHash<QUuid, SingleSenderStats>::const_iterator NodeToSenderStatsMapConstIterator;
/// Handles processing of incoming network packets for the octee servers. As with other ReceivedPacketProcessor classes
/// Handles processing of incoming network packets for the octee servers. As with other ReceivedPacketProcessor classes
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
class OctreeInboundPacketProcessor : public ReceivedPacketProcessor {
Q_OBJECT
@ -65,9 +65,9 @@ public:
quint64 getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
quint64 getTotalElementsProcessed() const { return _totalElementsInPacket; }
quint64 getTotalPacketsProcessed() const { return _totalPackets; }
quint64 getAverageProcessTimePerElement() const
quint64 getAverageProcessTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
quint64 getAverageLockWaitTimePerElement() const
quint64 getAverageLockWaitTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
void resetStats();
@ -78,7 +78,7 @@ public:
protected:
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
virtual unsigned long getMaxWait() const;
virtual void preProcess();
@ -88,13 +88,13 @@ private:
int sendNackPackets();
private:
void trackInboundPacket(const QUuid& nodeUUID, unsigned short int sequence, quint64 transitTime,
void trackInboundPacket(const QUuid& nodeUUID, unsigned short int sequence, quint64 transitTime,
int elementsInPacket, quint64 processTime, quint64 lockWaitTime);
OctreeServer* _myServer;
int _receivedPacketCount;
quint64 _totalTransitTime;
quint64 _totalTransitTime;
quint64 _totalProcessTime;
quint64 _totalLockWaitTime;
quint64 _totalElementsInPacket;

View file

@ -14,7 +14,7 @@
#include <cstring>
#include <cstdio>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include <UUID.h>
@ -22,11 +22,9 @@
OctreeQueryNode::OctreeQueryNode() :
_viewSent(false),
_octreePacket(new unsigned char[MAX_PACKET_SIZE]),
_octreePacketAt(_octreePacket),
_octreePacketAvailableBytes(MAX_PACKET_SIZE),
_octreePacket(),
_octreePacketWaiting(false),
_lastOctreePacket(new unsigned char[MAX_PACKET_SIZE]),
_lastOctreePayload(new char[MAX_PACKET_SIZE]),
_lastOctreePacketLength(0),
_duplicatePacketCount(0),
_firstSuppressedPacket(usecTimestampNow()),
@ -44,7 +42,7 @@ OctreeQueryNode::OctreeQueryNode() :
_lodInitialized(false),
_sequenceNumber(0),
_lastRootTimestamp(0),
_myPacketType(PacketTypeUnknown),
_myPacketType(PacketType::Unknown),
_isShuttingDown(false),
_sentPacketHistory()
{
@ -55,9 +53,8 @@ OctreeQueryNode::~OctreeQueryNode() {
if (_octreeSendThread) {
forceNodeShutdown();
}
delete[] _octreePacket;
delete[] _lastOctreePacket;
delete[] _lastOctreePayload;
}
void OctreeQueryNode::nodeKilled() {
@ -74,7 +71,7 @@ void OctreeQueryNode::forceNodeShutdown() {
_isShuttingDown = true;
elementBag.unhookNotifications(); // if our node is shutting down, then we no longer need octree element notifications
if (_octreeSendThread) {
// we really need to force our thread to shutdown, this is synchronous, we will block while the thread actually
// we really need to force our thread to shutdown, this is synchronous, we will block while the thread actually
// shuts down because we really need it to shutdown, and it's ok if we wait for it to complete
OctreeSendThread* sendThread = _octreeSendThread;
_octreeSendThread = NULL;
@ -96,8 +93,8 @@ void OctreeQueryNode::sendThreadFinished() {
}
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) {
_octreeSendThread = new OctreeSendThread(myServer, node);
_octreeSendThread = new OctreeSendThread(myServer, node);
// we want to be notified when the thread finishes
connect(_octreeSendThread, &GenericThread::finished, this, &OctreeQueryNode::sendThreadFinished);
_octreeSendThread->initialize(true);
@ -110,12 +107,11 @@ bool OctreeQueryNode::packetIsDuplicate() const {
}
// since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp
// of the entire packet, we need to compare only the packet content...
int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(_myPacketType);
if (_lastOctreePacketLength == getPacketLength()) {
if (memcmp(_lastOctreePacket + (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE),
_octreePacket + (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE),
getPacketLength() - (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE)) == 0) {
if (_lastOctreePacketLength == _octreePacket->getPayloadSize()) {
if (memcmp(_lastOctreePayload + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
_octreePacket->getPayload() + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
_octreePacket->getPayloadSize() - OCTREE_PACKET_EXTRA_HEADERS_SIZE) == 0) {
return true;
}
}
@ -163,6 +159,9 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() {
void OctreeQueryNode::init() {
_myPacketType = getMyPacketType();
_octreePacket = NLPacket::create(getMyPacketType());
resetOctreePacket(); // don't bump sequence
}
@ -177,8 +176,8 @@ void OctreeQueryNode::resetOctreePacket() {
// changed since we last reset it. Since we know that no two packets can ever be identical without being the same
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
// packet send rate.
_lastOctreePacketLength = getPacketLength();
memcpy(_lastOctreePacket, _octreePacket, _lastOctreePacketLength);
_lastOctreePacketLength = _octreePacket->getPayloadSize();
memcpy(_lastOctreePayload, _octreePacket->getPayload(), _lastOctreePacketLength);
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
@ -192,31 +191,17 @@ void OctreeQueryNode::resetOctreePacket() {
setAtBit(flags, PACKET_IS_COMPRESSED_BIT);
}
_octreePacketAvailableBytes = MAX_PACKET_SIZE;
int numBytesPacketHeader = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(_octreePacket),
_myPacketType);
_octreePacketAt = _octreePacket + numBytesPacketHeader;
_octreePacketAvailableBytes -= numBytesPacketHeader;
_octreePacket->reset();
// pack in flags
OCTREE_PACKET_FLAGS* flagsAt = (OCTREE_PACKET_FLAGS*)_octreePacketAt;
*flagsAt = flags;
_octreePacketAt += sizeof(OCTREE_PACKET_FLAGS);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_FLAGS);
_octreePacket->writePrimitive(flags);
// pack in sequence number
OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)_octreePacketAt;
*sequenceAt = _sequenceNumber;
_octreePacketAt += sizeof(OCTREE_PACKET_SEQUENCE);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SEQUENCE);
_octreePacket->writePrimitive(_sequenceNumber);
// pack in timestamp
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
OCTREE_PACKET_SENT_TIME* timeAt = (OCTREE_PACKET_SENT_TIME*)_octreePacketAt;
*timeAt = now;
_octreePacketAt += sizeof(OCTREE_PACKET_SENT_TIME);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SENT_TIME);
_octreePacket->writePrimitive(now);
_octreePacketWaiting = false;
}
@ -230,14 +215,11 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
// compressed packets include lead bytes which contain compressed size, this allows packing of
// multiple compressed portions together
if (_currentPacketIsCompressed) {
*(OCTREE_PACKET_INTERNAL_SECTION_SIZE*)_octreePacketAt = bytes;
_octreePacketAt += sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionSize = bytes;
_octreePacket->writePrimitive(sectionSize);
}
if (bytes <= _octreePacketAvailableBytes) {
memcpy(_octreePacketAt, buffer, bytes);
_octreePacketAvailableBytes -= bytes;
_octreePacketAt += bytes;
if (bytes <= _octreePacket->bytesAvailableForWrite()) {
_octreePacket->write(reinterpret_cast<const char*>(buffer), bytes);
_octreePacketWaiting = true;
}
}
@ -258,8 +240,8 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
float originalFOV = getCameraFov();
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
if (0.0f != getCameraAspectRatio() &&
0.0f != getCameraNearClip() &&
if (0.0f != getCameraAspectRatio() &&
0.0f != getCameraNearClip() &&
0.0f != getCameraFarClip()) {
newestViewFrustum.setProjection(glm::perspective(
glm::radians(wideFOV), // hack
@ -351,7 +333,7 @@ void OctreeQueryNode::dumpOutOfView() {
if (_isShuttingDown) {
return;
}
int stillInView = 0;
int outOfView = 0;
OctreeElementBag tempBag;
@ -374,15 +356,7 @@ void OctreeQueryNode::dumpOutOfView() {
}
}
void OctreeQueryNode::octreePacketSent() {
packetSent(_octreePacket, getPacketLength());
}
void OctreeQueryNode::packetSent(unsigned char* packet, int packetLength) {
packetSent(QByteArray((char*)packet, packetLength));
}
void OctreeQueryNode::packetSent(const QByteArray& packet) {
void OctreeQueryNode::packetSent(const NLPacket& packet) {
_sentPacketHistory.packetSent(_sequenceNumber, packet);
_sequenceNumber++;
}
@ -391,27 +365,20 @@ bool OctreeQueryNode::hasNextNackedPacket() const {
return !_nackedSequenceNumbers.isEmpty();
}
const QByteArray* OctreeQueryNode::getNextNackedPacket() {
const NLPacket* OctreeQueryNode::getNextNackedPacket() {
if (!_nackedSequenceNumbers.isEmpty()) {
// could return null if packet is not in the history
return _sentPacketHistory.getPacket(_nackedSequenceNumbers.dequeue());
}
return NULL;
return nullptr;
}
void OctreeQueryNode::parseNackPacket(const QByteArray& packet) {
int numBytesPacketHeader = numBytesForPacketHeader(packet);
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data()) + numBytesPacketHeader;
// read number of sequence numbers
uint16_t numSequenceNumbers = (*(uint16_t*)dataAt);
dataAt += sizeof(uint16_t);
void OctreeQueryNode::parseNackPacket(NLPacket& packet) {
// read sequence numbers
for (int i = 0; i < numSequenceNumbers; i++) {
OCTREE_PACKET_SEQUENCE sequenceNumber = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
while (packet.bytesLeftToRead()) {
OCTREE_PACKET_SEQUENCE sequenceNumber;
packet.readPrimitive(&sequenceNumber);
_nackedSequenceNumbers.enqueue(sequenceNumber);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
}
}

View file

@ -14,7 +14,6 @@
#include <iostream>
#include <CoverageMap.h>
#include <NodeData.h>
#include <OctreeConstants.h>
@ -35,20 +34,19 @@ public:
virtual ~OctreeQueryNode();
void init(); // called after creation to set up some virtual items
virtual PacketType getMyPacketType() const = 0;
virtual PacketType::Value getMyPacketType() const = 0;
void resetOctreePacket(); // resets octree packet to after "V" header
void writeToPacket(const unsigned char* buffer, unsigned int bytes); // writes to end of packet
const unsigned char* getPacket() const { return _octreePacket; }
unsigned int getPacketLength() const { return (MAX_PACKET_SIZE - _octreePacketAvailableBytes); }
NLPacket& getPacket() const { return *_octreePacket; }
bool isPacketWaiting() const { return _octreePacketWaiting; }
bool packetIsDuplicate() const;
bool shouldSuppressDuplicatePacket();
unsigned int getAvailable() const { return _octreePacketAvailableBytes; }
unsigned int getAvailable() const { return _octreePacket->bytesAvailableForWrite(); }
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
void incrementMaxSearchLevel() { _maxSearchLevel++; }
@ -62,7 +60,7 @@ public:
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }
// These are not classic setters because they are calculating and maintaining state
// which is set asynchronously through the network receive
bool updateCurrentViewFrustum();
@ -86,49 +84,46 @@ public:
}
bool hasLodChanged() const { return _lodChanged; }
OctreeSceneStats stats;
void initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
void dumpOutOfView();
quint64 getLastRootTimestamp() const { return _lastRootTimestamp; }
void setLastRootTimestamp(quint64 timestamp) { _lastRootTimestamp = timestamp; }
unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; }
int getDuplicatePacketCount() const { return _duplicatePacketCount; }
void sceneStart(quint64 sceneSendStartTime) { _sceneSendStartTime = sceneSendStartTime; }
void nodeKilled();
void forceNodeShutdown();
bool isShuttingDown() const { return _isShuttingDown; }
void octreePacketSent();
void packetSent(unsigned char* packet, int packetLength);
void packetSent(const QByteArray& packet);
void octreePacketSent() { packetSent(*_octreePacket); }
void packetSent(const NLPacket& packet);
OCTREE_PACKET_SEQUENCE getSequenceNumber() const { return _sequenceNumber; }
void parseNackPacket(const QByteArray& packet);
void parseNackPacket(NLPacket& packet);
bool hasNextNackedPacket() const;
const QByteArray* getNextNackedPacket();
const NLPacket* getNextNackedPacket();
private slots:
void sendThreadFinished();
private:
OctreeQueryNode(const OctreeQueryNode &);
OctreeQueryNode& operator= (const OctreeQueryNode&);
bool _viewSent;
unsigned char* _octreePacket;
unsigned char* _octreePacketAt;
unsigned int _octreePacketAvailableBytes;
std::unique_ptr<NLPacket> _octreePacket;
bool _octreePacketWaiting;
unsigned char* _lastOctreePacket;
char* _lastOctreePayload = nullptr;
unsigned int _lastOctreePacketLength;
int _duplicatePacketCount;
quint64 _firstSuppressedPacket;
@ -154,8 +149,8 @@ private:
OCTREE_PACKET_SEQUENCE _sequenceNumber;
quint64 _lastRootTimestamp;
PacketType _myPacketType;
PacketType::Value _myPacketType;
bool _isShuttingDown;
SentPacketHistory _sentPacketHistory;

View file

@ -11,7 +11,7 @@
#include <NodeList.h>
#include <NumericalConstants.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <PerfStat.h>
#include "OctreeSendThread.h"
@ -30,10 +30,10 @@ OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePoint
_isShuttingDown(false)
{
QString safeServerName("Octree");
// set our QThread object name so we can identify this thread while debugging
setObjectName(QString("Octree Send Thread (%1)").arg(uuidStringWithoutCurlyBraces(node->getUUID())));
if (_myServer) {
safeServerName = _myServer->getMyServerName();
}
@ -49,7 +49,7 @@ OctreeSendThread::~OctreeSendThread() {
if (_myServer) {
safeServerName = _myServer->getMyServerName();
}
qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected "
"- ending sending thread [" << this << "]";
@ -121,18 +121,18 @@ quint64 OctreeSendThread::_totalPackets = 0;
int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
OctreeServer::didHandlePacketSend(this);
// if we're shutting down, then exit early
// if we're shutting down, then exit early
if (nodeData->isShuttingDown()) {
return 0;
}
bool debug = _myServer->wantsDebugSending();
quint64 now = usecTimestampNow();
bool packetSent = false; // did we send a packet?
int packetsSent = 0;
// Here's where we check to see if this packet is a duplicate of the last packet. If it is, we will silently
// obscure the packet and not send it. This allows the callers and upper level logic to not need to know about
// this rate control savings.
@ -144,96 +144,100 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
// If we've got a stats message ready to send, then see if we can piggyback them together
if (nodeData->stats.isReadyToSend() && !nodeData->isShuttingDown()) {
// Send the stats message to the client
unsigned char* statsMessage = nodeData->stats.getStatsMessage();
int statsMessageLength = nodeData->stats.getStatsMessageLength();
int piggyBackSize = nodeData->getPacketLength() + statsMessageLength;
NLPacket& statsPacket = nodeData->stats.getStatsMessage();
// If the size of the stats message and the octree message will fit in a packet, then piggyback them
if (piggyBackSize < MAX_PACKET_SIZE) {
if (nodeData->getPacket().getDataSize() <= statsPacket.bytesAvailableForWrite()) {
// copy octree message to back of stats message
memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
statsMessageLength += nodeData->getPacketLength();
statsPacket.write(nodeData->getPacket().getData(), nodeData->getPacket().getDataSize());
// 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.
int thisWastedBytes = 0;
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacketLength();
_totalBytes += statsPacket.getDataSize();
_totalPackets++;
if (debug) {
const unsigned char* messageData = nodeData->getPacket();
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
const unsigned char* dataAt = messageData + numBytesPacketHeader;
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
NLPacket& sentPacket = nodeData->getPacket();
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
OCTREE_PACKET_SEQUENCE sequence;
sentPacket.readPrimitive(&sequence);
OCTREE_PACKET_SENT_TIME timestamp;
sentPacket.readPrimitive(&timestamp);
qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
" timestamp: " << timestamp <<
" statsMessageLength: " << statsMessageLength <<
" original size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
" statsMessageLength: " << statsPacket.getDataSize() <<
" original size: " << nodeData->getPacket().getDataSize() << " [" << _totalBytes <<
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
}
// actually send it
OctreeServer::didCallWriteDatagram(this);
DependencyManager::get<NodeList>()->writeDatagram((char*) statsMessage, statsMessageLength, _node);
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *_node);
packetSent = true;
} else {
// not enough room in the packet, send two packets
OctreeServer::didCallWriteDatagram(this);
DependencyManager::get<NodeList>()->writeDatagram((char*) statsMessage, statsMessageLength, _node);
DependencyManager::get<NodeList>()->sendUnreliablePacket(statsPacket, *_node);
// 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.
int thisWastedBytes = 0;
_totalWastedBytes += thisWastedBytes;
_totalBytes += statsMessageLength;
_totalBytes += statsPacket.getDataSize();
_totalPackets++;
if (debug) {
const unsigned char* messageData = nodeData->getPacket();
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
const unsigned char* dataAt = messageData + numBytesPacketHeader;
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
NLPacket& sentPacket = nodeData->getPacket();
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
OCTREE_PACKET_SEQUENCE sequence;
sentPacket.readPrimitive(&sequence);
OCTREE_PACKET_SENT_TIME timestamp;
sentPacket.readPrimitive(&timestamp);
qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
" timestamp: " << timestamp <<
" size: " << statsMessageLength << " [" << _totalBytes <<
" size: " << statsPacket.getDataSize() << " [" << _totalBytes <<
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
}
trueBytesSent += statsMessageLength;
trueBytesSent += statsPacket.getDataSize();
truePacketsSent++;
packetsSent++;
OctreeServer::didCallWriteDatagram(this);
DependencyManager::get<NodeList>()->writeDatagram((char*)nodeData->getPacket(), nodeData->getPacketLength(), _node);
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *_node);
packetSent = true;
thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacketLength();
_totalBytes += nodeData->getPacket().getDataSize();
_totalPackets++;
if (debug) {
const unsigned char* messageData = nodeData->getPacket();
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
const unsigned char* dataAt = messageData + numBytesPacketHeader;
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
NLPacket& sentPacket = nodeData->getPacket();
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
OCTREE_PACKET_SEQUENCE sequence;
sentPacket.readPrimitive(&sequence);
OCTREE_PACKET_SENT_TIME timestamp;
sentPacket.readPrimitive(&timestamp);
qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
" timestamp: " << timestamp <<
" size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
" size: " << nodeData->getPacket().getDataSize() << " [" << _totalBytes <<
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
}
}
@ -243,34 +247,38 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
// just send the octree packet
OctreeServer::didCallWriteDatagram(this);
DependencyManager::get<NodeList>()->writeDatagram((char*)nodeData->getPacket(), nodeData->getPacketLength(), _node);
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *_node);
packetSent = true;
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
int thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacketLength();
_totalBytes += packetSizeWithHeader;
_totalPackets++;
if (debug) {
const unsigned char* messageData = nodeData->getPacket();
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
const unsigned char* dataAt = messageData + numBytesPacketHeader;
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
NLPacket& sentPacket = nodeData->getPacket();
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
OCTREE_PACKET_SEQUENCE sequence;
sentPacket.readPrimitive(&sequence);
OCTREE_PACKET_SENT_TIME timestamp;
sentPacket.readPrimitive(&timestamp);
qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
" timestamp: " << timestamp <<
" size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
" size: " << packetSizeWithHeader << " [" << _totalBytes <<
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
}
}
}
// remember to track our stats
if (packetSent) {
nodeData->stats.packetSent(nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
nodeData->stats.packetSent(nodeData->getPacket().getPayloadSize());
trueBytesSent += nodeData->getPacket().getPayloadSize();
truePacketsSent++;
packetsSent++;
nodeData->octreePacketSent();
@ -282,14 +290,14 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
/// Version of octree element distributor that sends the deepest LOD level at once
int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged) {
OctreeServer::didPacketDistributor(this);
// if shutting down, exit early
if (nodeData->isShuttingDown()) {
return 0;
}
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
@ -297,7 +305,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
int truePacketsSent = 0;
int trueBytesSent = 0;
int packetsSentThisInterval = 0;
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging())
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging())
|| nodeData->hasLodChanged();
bool somethingToSend = true; // assume we have something
@ -395,18 +403,18 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
int extraPackingAttempts = 0;
bool completedScene = false;
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) {
float lockWaitElapsedUsec = OctreeServer::SKIP_TIME;
float encodeElapsedUsec = OctreeServer::SKIP_TIME;
float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME;
float packetSendingElapsedUsec = OctreeServer::SKIP_TIME;
quint64 startInside = usecTimestampNow();
quint64 startInside = usecTimestampNow();
bool lastNodeDidntFit = false; // assume each node fits
if (!nodeData->elementBag.isEmpty()) {
quint64 lockWaitStart = usecTimestampNow();
_myServer->getOctree()->lockForRead();
quint64 lockWaitEnd = usecTimestampNow();
@ -419,15 +427,15 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// going to result in any packets being sent...
//
// If our node is root, and the root hasn't changed, and our view hasn't changed,
// and we've already seen at least one duplicate packet, then we probably don't need
// to lock the tree and encode, because the result should be that no bytes will be
// and we've already seen at least one duplicate packet, then we probably don't need
// to lock the tree and encode, because the result should be that no bytes will be
// encoded, and this will be a duplicate packet from the last one we sent...
OctreeElement* root = _myServer->getOctree()->getRoot();
bool skipEncode = false;
if (
(subTree == root)
&& (nodeData->getLastRootTimestamp() == root->getLastChanged())
&& !viewFrustumChanged
&& !viewFrustumChanged
&& (nodeData->getDuplicatePacketCount() > 0)
) {
qDebug() << "is root, root not changed, view not changed, already seen a duplicate!"
@ -438,13 +446,13 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
float octreeSizeScale = nodeData->getOctreeSizeScale();
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving()
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
wantOcclusionCulling, coverageMap, boundaryLevelAdjust, octreeSizeScale,
@ -461,7 +469,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
quint64 encodeEnd = usecTimestampNow();
encodeElapsedUsec = (float)(encodeEnd - encodeStart);
// If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've
// sent the entire scene. We want to know this below so we'll actually write this content into
// the packet and send it
@ -502,7 +510,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
if (_packetData.hasContent()) {
quint64 compressAndWriteStart = usecTimestampNow();
// if for some reason the finalized size is greater than our available size, then probably the "compressed"
// form actually inflated beyond our padding, and in this case we will send the current packet, then
// write to out new packet...
@ -522,7 +530,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// If we're not running compressed, then we know we can just send now. Or if we're running compressed, but
// the packet doesn't have enough space to bother attempting to pack more...
bool sendNow = true;
if (nodeData->getCurrentPacketIsCompressed() &&
nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
@ -555,7 +563,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
OctreeServer::trackEncodeTime(encodeElapsedUsec);
OctreeServer::trackCompressAndWriteTime(compressAndWriteElapsedUsec);
OctreeServer::trackPacketSendingTime(packetSendingElapsedUsec);
quint64 endInside = usecTimestampNow();
quint64 elapsedInsideUsecs = endInside - startInside;
OctreeServer::trackInsideTime((float)elapsedInsideUsecs);
@ -572,9 +580,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// Here's where we can/should allow the server to send other data...
// send the environment packet
// TODO: should we turn this into a while loop to better handle sending multiple special packets
if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) {
if (_myServer->hasSpecialPacketsToSend(_node) && !nodeData->isShuttingDown()) {
int specialPacketsSent;
trueBytesSent += _myServer->sendSpecialPacket(_node, nodeData, specialPacketsSent);
trueBytesSent += _myServer->sendSpecialPackets(_node, nodeData, specialPacketsSent);
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
truePacketsSent += specialPacketsSent;
packetsSentThisInterval += specialPacketsSent;
@ -582,20 +590,20 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
// Re-send packets that were nacked by the client
while (nodeData->hasNextNackedPacket() && packetsSentThisInterval < maxPacketsPerInterval) {
const QByteArray* packet = nodeData->getNextNackedPacket();
const NLPacket* packet = nodeData->getNextNackedPacket();
if (packet) {
DependencyManager::get<NodeList>()->writeDatagram(*packet, _node);
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *_node);
truePacketsSent++;
packetsSentThisInterval++;
_totalBytes += packet->size();
_totalBytes += packet->getDataSize();
_totalPackets++;
_totalWastedBytes += MAX_PACKET_SIZE - packet->size();
_totalWastedBytes += MAX_PACKET_SIZE - packet->getDataSize();
}
}
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start)/USECS_PER_MSEC;
int elapsedmsec = (end - start) / USECS_PER_MSEC;
OctreeServer::trackLoopTime(elapsedmsec);
// TODO: add these to stats page

View file

@ -15,7 +15,6 @@
#define hifi_OctreeSendThread_h
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <OctreeElementBag.h>
#include "OctreeQueryNode.h"
@ -28,7 +27,7 @@ class OctreeSendThread : public GenericThread {
public:
OctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
virtual ~OctreeSendThread();
void setIsShuttingDown();
static quint64 _totalBytes;
@ -51,7 +50,7 @@ private:
int packetDistributor(OctreeQueryNode* nodeData, bool viewFrustumChanged);
OctreePacketData _packetData;
int _nodeMissingCount;
bool _isShuttingDown;
};

View file

@ -28,7 +28,6 @@
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
#include "OctreeServerDatagramProcessor.h"
OctreeServer* OctreeServer::_instance = NULL;
int OctreeServer::_clientCount = 0;
@ -213,7 +212,7 @@ void OctreeServer::trackProcessWaitTime(float time) {
_averageProcessWaitTime.updateAverage(time);
}
OctreeServer::OctreeServer(const QByteArray& packet) :
OctreeServer::OctreeServer(NLPacket& packet) :
ThreadedAssignment(packet),
_argc(0),
_argv(NULL),
@ -813,79 +812,31 @@ void OctreeServer::parsePayload() {
}
}
void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
auto nodeList = DependencyManager::get<NodeList>();
// If we know we're shutting down we just drop these packets on the floor.
// This stops us from initializing send threads we just shut down.
if (!_isShuttingDown) {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
PacketType packetType = packetTypeForPacket(receivedPacket);
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (packetType == getMyQueryMessageType()) {
// If we got a query packet, then we're talking to an agent, and we
// need to make sure we have it in our nodeList.
if (matchingNode) {
nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket);
OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData();
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
nodeData->initializeOctreeSendThread(this, matchingNode);
}
}
} else if (packetType == PacketTypeOctreeDataNack) {
// If we got a nack packet, then we're talking to an agent, and we
// need to make sure we have it in our nodeList.
if (matchingNode) {
OctreeQueryNode* nodeData = (OctreeQueryNode*)matchingNode->getLinkedData();
if (nodeData) {
nodeData->parseNackPacket(receivedPacket);
}
}
} else if (packetType == PacketTypeJurisdictionRequest) {
_jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket);
} else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) {
_octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket);
} else {
// let processNodeData handle it.
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, receivedPacket);
}
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
if (!_isFinished) {
// If we got a query packet, then we're talking to an agent, and we
// need to make sure we have it in our nodeList.
auto nodeList = DependencyManager::get<NodeList>();
nodeList->updateNodeWithDataFromPacket(packet, senderNode);
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(senderNode->getLinkedData());
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
nodeData->initializeOctreeSendThread(this, senderNode);
}
}
}
void OctreeServer::setupDatagramProcessingThread() {
auto nodeList = DependencyManager::get<NodeList>();
void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
// If we got a nack packet, then we're talking to an agent, and we
// need to make sure we have it in our nodeList.
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(senderNode->getLinkedData());
if (nodeData) {
nodeData->parseNackPacket(*packet);
}
}
// we do not want this event loop to be the handler for UDP datagrams, so disconnect
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
// setup a QThread with us as parent that will house the OctreeServerDatagramProcessor
_datagramProcessingThread = new QThread(this);
_datagramProcessingThread->setObjectName("Octree Datagram Processor");
// create an OctreeServerDatagramProcessor and move it to that thread
OctreeServerDatagramProcessor* datagramProcessor = new OctreeServerDatagramProcessor(nodeList->getNodeSocket(), thread());
datagramProcessor->moveToThread(_datagramProcessingThread);
// remove the NodeList as the parent of the node socket
nodeList->getNodeSocket().setParent(NULL);
nodeList->getNodeSocket().moveToThread(_datagramProcessingThread);
// let the datagram processor handle readyRead from node socket
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead,
datagramProcessor, &OctreeServerDatagramProcessor::readPendingDatagrams);
// connect to the datagram processing thread signal that tells us we have to handle a packet
connect(datagramProcessor, &OctreeServerDatagramProcessor::packetRequiresProcessing, this, &OctreeServer::readPendingDatagram);
// delete the datagram processor and the associated thread when the QThread quits
connect(_datagramProcessingThread, &QThread::finished, datagramProcessor, &QObject::deleteLater);
connect(datagramProcessor, &QObject::destroyed, _datagramProcessingThread, &QThread::deleteLater);
// start the datagram processing thread
_datagramProcessingThread->start();
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
_jurisdictionSender->queueReceivedPacket(packet, senderNode);
}
bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) {
@ -1078,6 +1029,12 @@ void OctreeServer::readConfiguration() {
}
void OctreeServer::run() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
_safeServerName = getMyServerName();
// Before we do anything else, create our tree...
@ -1093,15 +1050,13 @@ void OctreeServer::run() {
// use common init to setup common timers and logging
commonInit(getMyLoggingServerTargetName(), getMyNodeType());
setupDatagramProcessingThread();
// read the configuration from either the payload or the domain server configuration
readConfiguration();
beforeRun(); // after payload has been processed
connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)),SLOT(nodeKilled(SharedNodePointer)));
connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
// we need to ask the DS about agents so we can ping/reply with them
@ -1169,7 +1124,7 @@ void OctreeServer::nodeKilled(SharedNodePointer node) {
_octreeInboundPacketProcessor->nodeKilled(node);
qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(node->getLinkedData());
if (nodeData) {
nodeData->nodeKilled(); // tell our node data and sending threads that we'd like to shut down
} else {
@ -1187,7 +1142,7 @@ void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
quint64 start = usecTimestampNow();
qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(node->getLinkedData());
if (nodeData) {
nodeData->forceNodeShutdown(); // tell our node data and sending threads that we'd like to shut down
} else {

View file

@ -32,7 +32,7 @@ const int DEFAULT_PACKETS_PER_INTERVAL = 2000; // some 120,000 packets per secon
class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
Q_OBJECT
public:
OctreeServer(const QByteArray& packet);
OctreeServer(NLPacket& packet);
~OctreeServer();
/// allows setting of run arguments
@ -45,13 +45,13 @@ public:
Octree* getOctree() { return _tree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
int getPacketsPerClientPerInterval() const { return std::min(_packetsPerClientPerInterval,
int getPacketsPerClientPerInterval() const { return std::min(_packetsPerClientPerInterval,
std::max(1, getPacketsTotalPerInterval() / std::max(1, getCurrentClientCount()))); }
int getPacketsPerClientPerSecond() const { return getPacketsPerClientPerInterval() * INTERVALS_PER_SECOND; }
int getPacketsTotalPerInterval() const { return _packetsTotalPerInterval; }
int getPacketsTotalPerSecond() const { return getPacketsTotalPerInterval() * INTERVALS_PER_SECOND; }
static int getCurrentClientCount() { return _clientCount; }
static void clientConnected() { _clientCount++; }
static void clientDisconnected() { _clientCount--; }
@ -63,17 +63,17 @@ public:
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
virtual char getMyNodeType() const = 0;
virtual PacketType getMyQueryMessageType() const = 0;
virtual PacketType::Value getMyQueryMessageType() const = 0;
virtual const char* getMyServerName() const = 0;
virtual const char* getMyLoggingServerTargetName() const = 0;
virtual const char* getMyDefaultPersistFilename() const = 0;
virtual PacketType getMyEditNackType() const = 0;
virtual PacketType::Value getMyEditNackType() const = 0;
virtual QString getMyDomainSettingsKey() const { return QString("octree_server_settings"); }
// subclass may implement these method
virtual void beforeRun() { }
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; }
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; }
virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node) { return false; }
virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; }
static float SKIP_TIME; // use this for trackXXXTime() calls for non-times
@ -100,7 +100,7 @@ public:
static void trackProcessWaitTime(float time);
static float getAverageProcessWaitTime() { return _averageProcessWaitTime.getAverage(); }
// these methods allow us to track which threads got to various states
static void didProcess(OctreeSendThread* thread);
static void didPacketDistributor(OctreeSendThread* thread);
@ -117,16 +117,18 @@ public:
virtual void aboutToFinish();
void forceNodeShutdown(SharedNodePointer node);
public slots:
/// runs the octree server assignment
void run();
void nodeAdded(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
void sendStatsPacket();
void readPendingDatagrams() { }; // this will not be called since our datagram processing thread will handle
void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
private slots:
void handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleOctreeDataNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleJurisdictionRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
protected:
virtual Octree* createTree() = 0;
@ -143,8 +145,6 @@ protected:
QString getConfiguration();
QString getStatusLink();
void setupDatagramProcessingThread();
int _argc;
const char** _argv;
char** _parsedArgV;
@ -170,7 +170,7 @@ protected:
JurisdictionSender* _jurisdictionSender;
OctreeInboundPacketProcessor* _octreeInboundPacketProcessor;
OctreePersistThread* _persistThread;
int _persistInterval;
bool _wantBackup;
QString _backupExtensionFormat;
@ -182,7 +182,7 @@ protected:
time_t _started;
quint64 _startedUSecs;
QString _safeServerName;
static int _clientCount;
static SimpleMovingAverage _averageLoopTime;

View file

@ -1,55 +0,0 @@
//
// OctreeServerDatagramProcessor.cpp
// assignment-client/src
//
// Created by Brad Hefta-Gaub on 2014-09-05
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <HifiSockAddr.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include "OctreeServerDatagramProcessor.h"
OctreeServerDatagramProcessor::OctreeServerDatagramProcessor(QUdpSocket& nodeSocket, QThread* previousNodeSocketThread) :
_nodeSocket(nodeSocket),
_previousNodeSocketThread(previousNodeSocketThread)
{
}
OctreeServerDatagramProcessor::~OctreeServerDatagramProcessor() {
// return the node socket to its previous thread
_nodeSocket.moveToThread(_previousNodeSocketThread);
}
void OctreeServerDatagramProcessor::readPendingDatagrams() {
HifiSockAddr senderSockAddr;
static QByteArray incomingPacket;
// read everything that is available
while (_nodeSocket.hasPendingDatagrams()) {
incomingPacket.resize(_nodeSocket.pendingDatagramSize());
// just get this packet off the stack
_nodeSocket.readDatagram(incomingPacket.data(), incomingPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
PacketType packetType = packetTypeForPacket(incomingPacket);
if (packetType == PacketTypePing) {
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, incomingPacket);
return; // don't emit
}
// emit the signal to tell AudioMixer it needs to process a packet
emit packetRequiresProcessing(incomingPacket, senderSockAddr);
}
}

View file

@ -1,32 +0,0 @@
//
// OctreeServerDatagramProcessor.h
// assignment-client/src
//
// Created by Brad Hefta-Gaub on 2014-09-05
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_OctreeServerDatagramProcessor_h
#define hifi_OctreeServerDatagramProcessor_h
#include <qobject.h>
#include <qudpsocket.h>
class OctreeServerDatagramProcessor : public QObject {
Q_OBJECT
public:
OctreeServerDatagramProcessor(QUdpSocket& nodeSocket, QThread* previousNodeSocketThread);
~OctreeServerDatagramProcessor();
public slots:
void readPendingDatagrams();
signals:
void packetRequiresProcessing(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
private:
QUdpSocket& _nodeSocket;
QThread* _previousNodeSocketThread;
};
#endif // hifi_OctreeServerDatagramProcessor_h

View file

@ -29,7 +29,7 @@
#include <JSONBreakableMarshal.h>
#include <LogUtils.h>
#include <NetworkingConstants.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <SettingHandle.h>
#include <SharedUtil.h>
#include <ShutdownEventListener.h>
@ -111,6 +111,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
}
void DomainServer::aboutToQuit() {
// clear the log handler so that Qt doesn't call the destructor on LogHandler
qInstallMessageHandler(0);
}
@ -278,8 +279,17 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams()));
// register as the packet receiver for the types we want
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
packetReceiver.registerListener(PacketType::DomainConnectRequest, this, "processConnectRequestPacket");
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
packetReceiver.registerListener(PacketType::ICEPingReply, this, "processICEPingReplyPacket");
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, this, "processICEPeerInformationPacket");
// add whatever static assignments that have been parsed to the queue
addStaticAssignmentsToQueue();
}
@ -566,40 +576,52 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
<< NodeType::AvatarMixer << NodeType::EntityServer;
void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
NodeType_t nodeType;
HifiSockAddr publicSockAddr, localSockAddr;
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
if (packet->getPayloadSize() == 0) {
return;
}
parseNodeDataFromByteArray(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
QDataStream packetStream(packet.data());
QUuid packetUUID = uuidFromPacketHeader(packet);
QUuid connectUUID;
packetStream >> connectUUID;
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
parseNodeData(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
if (localSockAddr.isNull() || senderSockAddr.isNull()) {
qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection.";
return;
}
// check if this connect request matches an assignment in the queue
bool isAssignment = _pendingAssignedNodes.contains(packetUUID);
bool isAssignment = _pendingAssignedNodes.contains(connectUUID);
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
PendingAssignedNodeData* pendingAssigneeData = NULL;
if (isAssignment) {
pendingAssigneeData = _pendingAssignedNodes.value(packetUUID);
pendingAssigneeData = _pendingAssignedNodes.value(connectUUID);
if (pendingAssigneeData) {
matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType);
if (matchingQueuedAssignment) {
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID)
qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID)
<< "matches unfulfilled assignment"
<< uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID());
// remove this unique assignment deployment from the hash of pending assigned nodes
// cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList
_pendingAssignedNodes.remove(packetUUID);
_pendingAssignedNodes.remove(connectUUID);
} else {
// this is a node connecting to fulfill an assignment that doesn't exist
// don't reply back to them so they cycle back and re-request an assignment
qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID);
qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID);
return;
}
}
@ -617,11 +639,16 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
QString reason;
if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) {
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
QByteArray connectionDeniedByteArray = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied);
QDataStream out(&connectionDeniedByteArray, QIODevice::WriteOnly | QIODevice::Append);
out << reason;
QByteArray utfString = reason.toUtf8();
quint16 payloadSize = utfString.size();
auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize));
connectionDeniedPacket->writePrimitive(payloadSize);
connectionDeniedPacket->write(utfString);
// tell client it has been refused.
DependencyManager::get<LimitedNodeList>()->writeUnverifiedDatagram(connectionDeniedByteArray, senderSockAddr);
limitedNodeList->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
return;
}
@ -633,18 +660,18 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
QUuid nodeUUID;
HifiSockAddr discoveredSocket = senderSockAddr;
SharedNetworkPeer connectedPeer = _icePeers.value(packetUUID);
SharedNetworkPeer connectedPeer = _icePeers.value(connectUUID);
if (connectedPeer) {
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
nodeUUID = packetUUID;
nodeUUID = connectUUID;
if (connectedPeer->getActiveSocket()) {
// set their discovered socket to whatever the activated socket on the network peer object was
discoveredSocket = *connectedPeer->getActiveSocket();
}
} else {
// we got a packetUUID we didn't recognize, just add the node
// we got a connectUUID we didn't recognize, just add the node with a new UUID
nodeUUID = QUuid::createUuid();
}
@ -701,7 +728,7 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
nodeData->setSendingSockAddr(senderSockAddr);
// reply back to the user with a PacketTypeDomainList
// reply back to the user with a PacketType::DomainList
sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet());
// send out this node to our other connected nodes
@ -709,6 +736,24 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
}
}
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
NodeType_t throwawayNodeType;
HifiSockAddr nodePublicAddress, nodeLocalAddress;
QDataStream packetStream(packet.data());
parseNodeData(packetStream, throwawayNodeType, nodePublicAddress, nodeLocalAddress, packet->getSenderSockAddr());
sendingNode->setPublicSocket(nodePublicAddress);
sendingNode->setLocalSocket(nodeLocalAddress);
QList<NodeType_t> nodeInterestList;
packetStream >> nodeInterestList;
sendDomainListToNode(sendingNode, packet->getSenderSockAddr(), nodeInterestList.toSet());
}
unsigned int DomainServer::countConnectedUsers() {
unsigned int result = 0;
auto nodeList = DependencyManager::get<LimitedNodeList>();
@ -896,12 +941,12 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) {
return authorizationURL;
}
int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType_t& nodeType,
HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr,
const HifiSockAddr& senderSockAddr) {
int DomainServer::parseNodeData(QDataStream& packetStream, NodeType_t& nodeType,
HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr,
const HifiSockAddr& senderSockAddr) {
packetStream >> nodeType;
packetStream >> publicSockAddr >> localSockAddr;
if (publicSockAddr.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
@ -920,16 +965,24 @@ int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr,
const NodeSet& nodeInterestSet) {
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NUM_BYTES_RFC4122_UUID + 2;
// setup the extended header for the domain list packets
// this data is at the beginning of each of the domain list packets
QByteArray extendedHeader(NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES, 0);
QDataStream extendedHeaderStream(&extendedHeader, QIODevice::WriteOnly);
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
QByteArray broadcastPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainList);
extendedHeaderStream << limitedNodeList->getSessionUUID();
extendedHeaderStream << node->getUUID();
extendedHeaderStream << (quint8) node->getCanAdjustLocks();
extendedHeaderStream << (quint8) node->getCanRez();
NLPacketList domainListPackets(PacketType::DomainList, extendedHeader);
// always send the node their own UUID back
QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append);
broadcastDataStream << node->getUUID();
broadcastDataStream << node->getCanAdjustLocks();
broadcastDataStream << node->getCanRez();
int numBroadcastPacketLeadBytes = broadcastDataStream.device()->pos();
QDataStream domainListStream(&domainListPackets);
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
@ -938,44 +991,32 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
if (nodeInterestSet.size() > 0) {
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
int dataMTU = MAX_PACKET_SIZE;
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
if (nodeData->isAuthenticated()) {
// if this authenticated node has any interest types, send back those nodes as well
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){
// reset our nodeByteArray and nodeDataStream
QByteArray nodeByteArray;
QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append);
if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) {
// since we're about to add a node to the packet we start a segment
domainListPackets.startSegment();
// don't send avatar nodes to other avatars, that will come from avatar mixer
nodeDataStream << *otherNode.data();
domainListStream << *otherNode.data();
// pack the secret that these two nodes will use to communicate with each other
nodeDataStream << connectionSecretForNodes(node, otherNode);
domainListStream << connectionSecretForNodes(node, otherNode);
if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) {
// we need to break here and start a new packet
// so send the current one
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr);
// reset the broadcastPacket structure
broadcastPacket.resize(numBroadcastPacketLeadBytes);
broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes);
}
// append the nodeByteArray to the current state of broadcastDataStream
broadcastPacket.append(nodeByteArray);
// we've added the node we wanted so end the segment now
domainListPackets.endSegment();
}
});
}
}
// send an empty list to the node, in case there were no other nodes
domainListPackets.closeCurrentPacket(true);
// always write the last broadcastPacket
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node);
// write the PacketList to this node
limitedNodeList->sendPacketList(domainListPackets, *node);
}
QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
@ -1003,13 +1044,14 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
auto addNodePacket = NLPacket::create(PacketType::DomainServerAddedNode);
// setup the add packet for this new node
QByteArray addNodePacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerAddedNode);
QDataStream addNodeStream(&addNodePacket, QIODevice::Append);
QDataStream addNodeStream(addNodePacket.get());
addNodeStream << *addedNode.data();
int connectionSecretIndex = addNodePacket.size();
int connectionSecretIndex = addNodePacket->pos();
limitedNodeList->eachMatchingNode(
[&](const SharedNodePointer& node)->bool {
@ -1022,104 +1064,81 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
}
},
[&](const SharedNodePointer& node) {
addNodePacket->seek(connectionSecretIndex);
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
// replace the bytes at the end of the packet for the connection secret between these nodes
addNodePacket.replace(connectionSecretIndex, NUM_BYTES_RFC4122_UUID, rfcConnectionSecret);
addNodePacket->write(rfcConnectionSecret);
// send off this packet to the node
limitedNodeList->writeUnverifiedDatagram(addNodePacket, node);
limitedNodeList->sendUnreliablePacket(*addNodePacket, *node);
}
);
}
void DomainServer::readAvailableDatagrams() {
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
void DomainServer::processRequestAssignmentPacket(QSharedPointer<NLPacket> packet) {
// construct the requested assignment from the packet data
Assignment requestAssignment(*packet);
HifiSockAddr senderSockAddr;
QByteArray receivedPacket;
// Suppress these for Assignment::AgentType to once per 5 seconds
static QElapsedTimer noisyMessageTimer;
static bool wasNoisyTimerStarted = false;
static QByteArray assignmentPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeCreateAssignment);
static int numAssignmentPacketHeaderBytes = assignmentPacket.size();
if (!wasNoisyTimerStarted) {
noisyMessageTimer.start();
wasNoisyTimerStarted = true;
}
while (limitedNodeList->getNodeSocket().hasPendingDatagrams()) {
receivedPacket.resize(limitedNodeList->getNodeSocket().pendingDatagramSize());
limitedNodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
if (packetTypeForPacket(receivedPacket) == PacketTypeRequestAssignment
&& limitedNodeList->packetVersionAndHashMatch(receivedPacket)) {
const qint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000;
// construct the requested assignment from the packet data
Assignment requestAssignment(receivedPacket);
if (requestAssignment.getType() != Assignment::AgentType
|| noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) {
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
("Received a request for assignment type [^ ]+ from [^ ]+");
qDebug() << "Received a request for assignment type" << requestAssignment.getType()
<< "from" << packet->getSenderSockAddr();
noisyMessageTimer.restart();
}
// Suppress these for Assignment::AgentType to once per 5 seconds
static QElapsedTimer noisyMessageTimer;
static bool wasNoisyTimerStarted = false;
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
if (!wasNoisyTimerStarted) {
noisyMessageTimer.start();
wasNoisyTimerStarted = true;
}
if (assignmentToDeploy) {
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << packet->getSenderSockAddr();
const qint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000;
// give this assignment out, either the type matches or the requestor said they will take any
static std::unique_ptr<NLPacket> assignmentPacket;
if (requestAssignment.getType() != Assignment::AgentType
|| noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) {
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
("Received a request for assignment type [^ ]+ from [^ ]+");
qDebug() << "Received a request for assignment type" << requestAssignment.getType()
<< "from" << senderSockAddr;
noisyMessageTimer.restart();
}
if (!assignmentPacket) {
assignmentPacket = NLPacket::create(PacketType::CreateAssignment);
}
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
// setup a copy of this assignment that will have a unique UUID, for packaging purposes
Assignment uniqueAssignment(*assignmentToDeploy.data());
uniqueAssignment.setUUID(QUuid::createUuid());
if (assignmentToDeploy) {
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr;
// reset the assignmentPacket
assignmentPacket->reset();
// give this assignment out, either the type matches or the requestor said they will take any
assignmentPacket.resize(numAssignmentPacketHeaderBytes);
QDataStream assignmentStream(assignmentPacket.get());
// setup a copy of this assignment that will have a unique UUID, for packaging purposes
Assignment uniqueAssignment(*assignmentToDeploy.data());
uniqueAssignment.setUUID(QUuid::createUuid());
assignmentStream << uniqueAssignment;
QDataStream assignmentStream(&assignmentPacket, QIODevice::Append);
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
limitedNodeList->sendUnreliablePacket(*assignmentPacket, packet->getSenderSockAddr());
assignmentStream << uniqueAssignment;
limitedNodeList->getNodeSocket().writeDatagram(assignmentPacket,
senderSockAddr.getAddress(), senderSockAddr.getPort());
// add the information for that deployed assignment to the hash of pending assigned nodes
PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(),
requestAssignment.getWalletUUID());
_pendingAssignedNodes.insert(uniqueAssignment.getUUID(), pendingNodeData);
} else {
if (requestAssignment.getType() != Assignment::AgentType
|| noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) {
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
("Unable to fulfill assignment request of type [^ ]+ from [^ ]+");
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
<< "from" << senderSockAddr;
noisyMessageTimer.restart();
}
}
} else if (!_isUsingDTLS) {
// not using DTLS, process datagram normally
processDatagram(receivedPacket, senderSockAddr);
} else {
// we're using DTLS, so tell the sender to get back to us using DTLS
static QByteArray dtlsRequiredPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS);
static int numBytesDTLSHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerRequireDTLS);
if (dtlsRequiredPacket.size() == numBytesDTLSHeader) {
// pack the port that we accept DTLS traffic on
unsigned short dtlsPort = limitedNodeList->getDTLSSocket().localPort();
dtlsRequiredPacket.replace(numBytesDTLSHeader, sizeof(dtlsPort), reinterpret_cast<const char*>(&dtlsPort));
}
limitedNodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr);
// add the information for that deployed assignment to the hash of pending assigned nodes
PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(),
requestAssignment.getWalletUUID());
_pendingAssignedNodes.insert(uniqueAssignment.getUUID(), pendingNodeData);
} else {
if (requestAssignment.getType() != Assignment::AgentType
|| noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) {
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
("Unable to fulfill assignment request of type [^ ]+ from [^ ]+");
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
<< "from" << packet->getSenderSockAddr();
noisyMessageTimer.restart();
}
}
}
@ -1322,14 +1341,14 @@ void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) {
_icePeers.remove(peer->getUUID());
} else {
auto nodeList = DependencyManager::get<LimitedNodeList>();
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
// send the ping packet to the local and public sockets for this node
QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false);
nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket());
auto localPingPacket = limitedNodeList->constructICEPingPacket(PingType::Local, limitedNodeList->getSessionUUID());
limitedNodeList->sendPacket(std::move(localPingPacket), peer->getLocalSocket());
QByteArray publicPingPacket = nodeList->constructPingPacket(PingType::Public, false);
nodeList->writeUnverifiedDatagram(publicPingPacket, peer->getPublicSocket());
auto publicPingPacket = limitedNodeList->constructICEPingPacket(PingType::Public, limitedNodeList->getSessionUUID());
limitedNodeList->sendPacket(std::move(publicPingPacket), peer->getPublicSocket());
peer->incrementConnectionAttempts();
}
@ -1347,11 +1366,10 @@ void DomainServer::handlePeerPingTimeout() {
}
}
void DomainServer::processICEPeerInformation(const QByteArray& packet) {
void DomainServer::processICEPeerInformationPacket(QSharedPointer<NLPacket> packet) {
// loop through the packet and pull out network peers
// any peer we don't have we add to the hash, otherwise we update
QDataStream iceResponseStream(packet);
iceResponseStream.skipRawData(numBytesForPacketHeader(packet));
QDataStream iceResponseStream(packet.data());
NetworkPeer* receivedPeer = new NetworkPeer;
iceResponseStream >> *receivedPeer;
@ -1376,86 +1394,31 @@ void DomainServer::processICEPeerInformation(const QByteArray& packet) {
}
}
void DomainServer::processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
QUuid nodeUUID = uuidFromPacketHeader(packet);
void DomainServer::processICEPingPacket(QSharedPointer<NLPacket> packet) {
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*packet, limitedNodeList->getSessionUUID());
limitedNodeList->sendPacket(std::move(pingReplyPacket), packet->getSenderSockAddr());
}
void DomainServer::processICEPingReplyPacket(QSharedPointer<NLPacket> packet) {
QDataStream packetStream(packet.data());
QUuid nodeUUID;
packetStream >> nodeUUID;
SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID);
if (sendingPeer) {
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
sendingPeer->activateMatchingOrNewSymmetricSocket(senderSockAddr);
sendingPeer->activateMatchingOrNewSymmetricSocket(packet->getSenderSockAddr());
}
}
void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
auto nodeList = DependencyManager::get<LimitedNodeList>();
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
PacketType requestType = packetTypeForPacket(receivedPacket);
switch (requestType) {
case PacketTypeDomainConnectRequest:
handleConnectRequest(receivedPacket, senderSockAddr);
break;
case PacketTypeDomainListRequest: {
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) {
NodeType_t throwawayNodeType;
HifiSockAddr nodePublicAddress, nodeLocalAddress;
QDataStream packetStream(receivedPacket);
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
parseNodeDataFromByteArray(packetStream, throwawayNodeType, nodePublicAddress, nodeLocalAddress,
senderSockAddr);
SharedNodePointer checkInNode = nodeList->nodeWithUUID(nodeUUID);
checkInNode->setPublicSocket(nodePublicAddress);
checkInNode->setLocalSocket(nodeLocalAddress);
// update last receive to now
quint64 timeNow = usecTimestampNow();
checkInNode->setLastHeardMicrostamp(timeNow);
QList<NodeType_t> nodeInterestList;
packetStream >> nodeInterestList;
sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestList.toSet());
}
break;
}
case PacketTypeDomainServerPathQuery: {
// have our private method attempt to respond to this path query
respondToPathQuery(receivedPacket, senderSockAddr);
break;
}
case PacketTypeNodeJsonStats: {
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (matchingNode) {
reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData())->parseJSONStatsPacket(receivedPacket);
}
break;
}
case PacketTypeStunResponse:
nodeList->processSTUNResponse(receivedPacket);
break;
case PacketTypeUnverifiedPing: {
QByteArray pingReplyPacket = nodeList->constructPingReplyPacket(receivedPacket);
nodeList->writeUnverifiedDatagram(pingReplyPacket, senderSockAddr);
break;
}
case PacketTypeUnverifiedPingReply: {
processICEPingReply(receivedPacket, senderSockAddr);
break;
}
case PacketTypeIceServerPeerInformation:
processICEPeerInformation(receivedPacket);
break;
default:
break;
}
void DomainServer::processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
auto nodeData = dynamic_cast<DomainServerNodeData*>(sendingNode->getLinkedData());
if (nodeData) {
nodeData->processJSONStatsPacket(*packet);
}
}
@ -2223,19 +2186,17 @@ void DomainServer::addStaticAssignmentsToQueue() {
}
}
void DomainServer::respondToPathQuery(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
// this is a query for the viewpoint resulting from a path
// first pull the query path from the packet
int numHeaderBytes = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerPathQuery);
const char* packetDataStart = receivedPacket.data() + numHeaderBytes;
// figure out how many bytes the sender said this path is
quint16 numPathBytes = *packetDataStart;
quint16 numPathBytes;
packet->readPrimitive(&numPathBytes);
if (numPathBytes <= receivedPacket.size() - numHeaderBytes - sizeof(numPathBytes)) {
if (numPathBytes <= packet->bytesLeftToRead()) {
// the number of path bytes makes sense for the sent packet - pull out the path
QString pathQuery = QString::fromUtf8(packetDataStart + sizeof(numPathBytes), numPathBytes);
QString pathQuery = QString::fromUtf8(packet->getPayload() + packet->pos(), numPathBytes);
// our settings contain paths that start with a leading slash, so make sure this query has that
if (!pathQuery.startsWith("/")) {
@ -2259,31 +2220,31 @@ void DomainServer::respondToPathQuery(const QByteArray& receivedPacket, const Hi
QByteArray viewpointUTF8 = responseViewpoint.toUtf8();
// prepare a packet for the response
QByteArray pathResponsePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerPathResponse);
auto pathResponsePacket = NLPacket::create(PacketType::DomainServerPathResponse);
// check the number of bytes the viewpoint is
quint16 numViewpointBytes = responseViewpoint.toUtf8().size();
quint16 numViewpointBytes = viewpointUTF8.size();
// are we going to be able to fit this response viewpoint in a packet?
if (numPathBytes + numViewpointBytes + pathResponsePacket.size() + sizeof(numViewpointBytes)
< MAX_PACKET_SIZE) {
if (numPathBytes + numViewpointBytes + sizeof(numViewpointBytes) + sizeof(numPathBytes)
< (unsigned long) pathResponsePacket->bytesAvailableForWrite()) {
// append the number of bytes this path is
pathResponsePacket.append(reinterpret_cast<char*>(&numPathBytes), sizeof(numPathBytes));
pathResponsePacket->writePrimitive(numPathBytes);
// append the path itself
pathResponsePacket.append(pathQuery.toUtf8());
pathResponsePacket->write(pathQuery.toUtf8());
// append the number of bytes the resulting viewpoint is
pathResponsePacket.append(reinterpret_cast<char*>(&numViewpointBytes), sizeof(numViewpointBytes));
pathResponsePacket->writePrimitive(numViewpointBytes);
// append the viewpoint itself
pathResponsePacket.append(viewpointUTF8);
pathResponsePacket->write(viewpointUTF8);
qDebug() << "Sending a viewpoint response for path query" << pathQuery << "-" << viewpointUTF8;
// send off the packet - see if we can associate this outbound data to a particular node
// TODO: does this senderSockAddr always work for a punched DS client?
nodeList->writeUnverifiedDatagram(pathResponsePacket, senderSockAddr);
nodeList->sendPacket(std::move(pathResponsePacket), packet->getSenderSockAddr());
}
}

View file

@ -34,7 +34,6 @@
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
Q_OBJECT
public:
@ -56,11 +55,19 @@ public slots:
void restart();
void processRequestAssignmentPacket(QSharedPointer<NLPacket> packet);
void processConnectRequestPacket(QSharedPointer<NLPacket> packet);
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
void processICEPingPacket(QSharedPointer<NLPacket> packet);
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
void processICEPeerInformationPacket(QSharedPointer<NLPacket> packet);
private slots:
void aboutToQuit();
void loginFailed();
void readAvailableDatagrams();
void setupPendingAssignmentCredits();
void sendPendingTransactionsToServer();
@ -79,14 +86,9 @@ private:
void setupAutomaticNetworking();
void sendHeartbeatToDataServer(const QString& networkAddress);
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
void processICEPeerInformation(const QByteArray& packet);
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
unsigned int countConnectedUsers();
bool verifyUsersKey (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn);
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
@ -95,11 +97,8 @@ private:
void preloadAllowedUserPublicKeys();
void requestUserPublicKey(const QString& username);
int parseNodeDataFromByteArray(QDataStream& packetStream,
NodeType_t& nodeType,
HifiSockAddr& publicSockAddr,
HifiSockAddr& localSockAddr,
const HifiSockAddr& senderSockAddr);
int parseNodeData(QDataStream& packetStream, NodeType_t& nodeType,
HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, const HifiSockAddr& senderSockAddr);
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
const NodeSet& nodeInterestSet);
@ -118,8 +117,6 @@ private:
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
void addStaticAssignmentsToQueue();
void respondToPathQuery(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
QUrl oauthRedirectURL();
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());

View file

@ -14,7 +14,7 @@
#include <QtCore/QVariant>
#include <JSONBreakableMarshal.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include "DomainServerNodeData.h"
@ -31,8 +31,8 @@ DomainServerNodeData::DomainServerNodeData() :
_paymentIntervalTimer.start();
}
void DomainServerNodeData::parseJSONStatsPacket(const QByteArray& statsPacket) {
QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(statsPacket.mid(numBytesForPacketHeader(statsPacket)));
void DomainServerNodeData::processJSONStatsPacket(NLPacket& statsPacket) {
QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(statsPacket.readAll());
_statsJSONObject = mergeJSONStatsFromNewObject(QJsonObject::fromVariantMap(packetVariantMap), _statsJSONObject);
}

View file

@ -17,17 +17,17 @@
#include <QtCore/QUuid>
#include <HifiSockAddr.h>
#include <NLPacket.h>
#include <NodeData.h>
#include <NodeType.h>
class DomainServerNodeData : public NodeData {
public:
DomainServerNodeData();
int parseData(const QByteArray& packet) { return 0; }
const QJsonObject& getStatsJSONObject() const { return _statsJSONObject; }
void parseJSONStatsPacket(const QByteArray& statsPacket);
void processJSONStatsPacket(NLPacket& packet);
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }

View file

@ -12,7 +12,7 @@
#include <QTimer>
#include <LimitedNodeList.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include "IceServer.h"
@ -46,41 +46,45 @@ IceServer::IceServer(int argc, char* argv[]) :
void IceServer::processDatagrams() {
HifiSockAddr sendingSockAddr;
QByteArray incomingPacket;
while (_serverSocket.hasPendingDatagrams()) {
incomingPacket.resize(_serverSocket.pendingDatagramSize());
// setup a buffer to read the packet into
int packetSizeWithHeader = _serverSocket.pendingDatagramSize();
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[packetSizeWithHeader]);
_serverSocket.readDatagram(incomingPacket.data(), incomingPacket.size(),
_serverSocket.readDatagram(buffer.get(), packetSizeWithHeader,
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, sendingSockAddr);
PacketType packetType = packetTypeForPacket(incomingPacket);
PacketType::Value packetType = packet->getType();
if (packetType == PacketTypeIceServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(incomingPacket);
if (packetType == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*packet);
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
} else if (packetType == PacketTypeIceServerQuery) {
} else if (packetType == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(packet.get());
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
QUuid senderUUID = uuidFromPacketHeader(incomingPacket);
QUuid senderUUID;
heartbeatStream >> senderUUID;
// pull the public and private sock addrs for this peer
HifiSockAddr publicSocket, localSocket;
QDataStream hearbeatStream(incomingPacket);
hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket));
hearbeatStream >> publicSocket >> localSocket;
heartbeatStream >> publicSocket >> localSocket;
// check if this node also included a UUID that they would like to connect to
QUuid connectRequestID;
hearbeatStream >> connectRequestID;
heartbeatStream >> connectRequestID;
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
if (matchingPeer) {
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
// we have the peer they want to connect to - send them pack the information for that peer
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
@ -89,21 +93,23 @@ void IceServer::processDatagrams() {
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
} else {
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
}
}
}
}
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket) {
QUuid senderUUID = uuidFromPacketHeader(incomingPacket);
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(Packet& packet) {
// pull the public and private sock addrs for this peer
// pull the UUID, public and private sock addrs for this peer
QUuid senderUUID;
HifiSockAddr publicSocket, localSocket;
QDataStream hearbeatStream(incomingPacket);
hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket));
hearbeatStream >> publicSocket >> localSocket;
QDataStream heartbeatStream(&packet);
heartbeatStream >> senderUUID;
heartbeatStream >> publicSocket >> localSocket;
// make sure we have this sender in our peer hash
SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
@ -127,18 +133,13 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(const QByteArray& incom
}
void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {
QByteArray outgoingPacket(MAX_PACKET_SIZE, 0);
int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerPeerInformation, _id);
int numHeaderBytes = currentPacketSize;
auto peerPacket = Packet::create(PacketType::ICEServerPeerInformation);
// get the byte array for this peer
QByteArray peerBytes = peer.toByteArray();
outgoingPacket.replace(numHeaderBytes, peerBytes.size(), peerBytes);
currentPacketSize += peerBytes.size();
peerPacket->write(peer.toByteArray());
// write the current packet
_serverSocket.writeDatagram(outgoingPacket.data(), outgoingPacket.size(),
_serverSocket.writeDatagram(peerPacket->getData(), peerPacket->getDataSize(),
destinationSockAddr->getAddress(), destinationSockAddr->getPort());
}

View file

@ -19,6 +19,7 @@
#include <NetworkPeer.h>
#include <HTTPConnection.h>
#include <HTTPManager.h>
#include <udt/Packet.h>
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
@ -32,7 +33,7 @@ private slots:
void clearInactivePeers();
private:
SharedNetworkPeer addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket);
SharedNetworkPeer addOrUpdateHeartbeatingPeer(Packet& incomingPacket);
void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
QUuid _id;

View file

@ -22,23 +22,13 @@ else ()
set(BUILD_SEQ "dev")
endif ()
if (APPLE)
set(GL_HEADERS "#include <OpenGL/glext.h>")
elseif (UNIX)
# include the right GL headers for UNIX
set(GL_HEADERS "#include <GL/gl.h>\n#include <GL/glext.h>")
elseif (WIN32)
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
if (WIN32)
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
endif()
set(GL_HEADERS "#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/wglew.h>")
endif ()
# create the InterfaceConfig.h file based on GL_HEADERS above
configure_file(InterfaceConfig.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h")
configure_file(InterfaceVersion.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceVersion.h")
macro(GroupSources curdir)
file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*)
foreach(child ${children})
@ -105,10 +95,10 @@ if (APPLE)
# set where in the bundle to put the resources file
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(DISCOVERED_RESOURCES "")
set(DISCOVERED_RESOURCES "")
# use the add_resources_to_os_x_bundle macro to recurse into resources
add_resources_to_os_x_bundle("${CMAKE_CURRENT_SOURCE_DIR}/resources")
add_resources_to_os_x_bundle("${CMAKE_CURRENT_SOURCE_DIR}/resources")
# append the discovered resources to our list of interface sources
list(APPEND INTERFACE_SRCS ${DISCOVERED_RESOURCES})
@ -136,12 +126,19 @@ target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
find_package(Bullet REQUIRED)
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
# perform the system include hack for OS X to ignore warnings
if (APPLE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${BULLET_INCLUDE_DIRS}")
else()
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
endif()
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
# link required hifi libraries
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
audio audio-client animation script-engine physics
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater)
add_dependency_external_projects(sdl2)
@ -185,7 +182,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
endforeach()
# special OS X modifications for RtMidi library
if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE)
if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE)
find_library(CoreMIDI CoreMIDI)
add_definitions(-D__MACOSX_CORE__)
target_link_libraries(${TARGET_NAME} ${CoreMIDI})

View file

@ -1,18 +0,0 @@
//
// InterfaceConfig.h
// interface/src
//
// Created by Stephen Birarda on 2/8/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef interface__InterfaceConfig__
#define interface__InterfaceConfig__
#define GL_GLEXT_PROTOTYPES 1
@GL_HEADERS@
#endif

File diff suppressed because it is too large Load diff

View file

@ -12,8 +12,6 @@
#ifndef hifi_Application_h
#define hifi_Application_h
#include <gpu/GPUConfig.h>
#include <QApplication>
#include <QHash>
#include <QImage>
@ -28,28 +26,24 @@
#include <EntityEditPacketSender.h>
#include <EntityTreeRenderer.h>
#include <GeometryCache.h>
#include <NetworkPacket.h>
#include <NodeList.h>
#include <OctreeQuery.h>
#include <OffscreenUi.h>
#include <PacketHeaders.h>
#include <PhysicalEntitySimulation.h>
#include <PhysicsEngine.h>
#include <ScriptEngine.h>
#include <ShapeManager.h>
#include <StDev.h>
#include <TextureCache.h>
#include <udt/PacketHeaders.h>
#include <ViewFrustum.h>
#include "AudioClient.h"
#include "Bookmarks.h"
#include "Camera.h"
#include "DatagramProcessor.h"
#include "Environment.h"
#include "FileLogger.h"
#include "GLCanvas.h"
#include "Menu.h"
#include "PacketHeaders.h"
#include "Physics.h"
#include "Stars.h"
#include "avatar/Avatar.h"
@ -84,10 +78,10 @@ class QSystemTrayIcon;
class QTouchEvent;
class QWheelEvent;
class GLCanvas;
class FaceTracker;
class MainWindow;
class Node;
class ProgramObject;
class ScriptEngine;
static const QString SNAPSHOT_EXTENSION = ".jpg";
@ -114,7 +108,7 @@ static const QString INFO_HELP_PATH = "html/interface-welcome.html";
static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
#ifdef Q_OS_WIN
static const UINT UWM_IDENTIFY_INSTANCES =
static const UINT UWM_IDENTIFY_INSTANCES =
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
static const UINT UWM_SHOW_APPLICATION =
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME"));
@ -128,7 +122,7 @@ class Application;
typedef bool (Application::* AcceptURLMethod)(const QString &);
class Application : public QApplication, public AbstractViewStateInterface, AbstractScriptingServicesInterface {
class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface {
Q_OBJECT
friend class OctreePacketProcessor;
@ -191,11 +185,11 @@ public:
bool isThrottleRendering() const;
Camera* getCamera() { return &_myCamera; }
// Represents the current view frustum of the avatar.
// Represents the current view frustum of the avatar.
ViewFrustum* getViewFrustum();
const ViewFrustum* getViewFrustum() const;
// Represents the view frustum of the current rendering pass,
// which might be different from the viewFrustum, i.e. shadowmap
// Represents the view frustum of the current rendering pass,
// which might be different from the viewFrustum, i.e. shadowmap
// passes, mirror window passes, etc
ViewFrustum* getDisplayViewFrustum();
const ViewFrustum* getDisplayViewFrustum() const;
@ -207,7 +201,8 @@ public:
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
EntityTree* getEntityClipboard() { return &_entityClipboard; }
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
bool isMousePressed() const { return _mousePressed; }
bool isMouseHidden() const { return !_cursorVisible; }
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
@ -229,7 +224,7 @@ public:
int getTrueMouseDragStartedX() const { return getTrueMouseDragStarted().x; }
int getTrueMouseDragStartedY() const { return getTrueMouseDragStarted().y; }
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
FaceTracker* getActiveFaceTracker();
FaceTracker* getSelectedFaceTracker();
@ -246,7 +241,7 @@ public:
virtual const Transform& getViewTransform() const { return _viewTransform; }
void setViewTransform(const Transform& view);
float getFieldOfView() { return _fieldOfView.get(); }
void setFieldOfView(float fov) { _fieldOfView.set(fov); }
@ -263,9 +258,7 @@ public:
void resetProfile(const QString& username);
void controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
virtual void setupWorldLight();
virtual void setupWorldLight(RenderArgs* renderArgs);
virtual bool shouldRenderMesh(float largestDimension, float distanceToCamera);
QImage renderAvatarBillboard(RenderArgs* renderArgs);
@ -311,7 +304,7 @@ public:
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; }
bool isLookingAtMyAvatar(Avatar* avatar);
float getRenderResolutionScale() const;
@ -336,17 +329,17 @@ public:
RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; }
Bookmarks* getBookmarks() const { return _bookmarks; }
QString getScriptsLocation();
void setScriptsLocation(const QString& scriptsLocation);
void initializeAcceptedFiles();
bool canAcceptURL(const QString& url);
bool acceptURL(const QString& url);
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
int getMaxOctreePacketsPerSecond();
render::ScenePointer getMain3DScene() { return _main3DScene; }
render::EnginePointer getRenderEngine() { return _renderEngine; }
@ -362,7 +355,7 @@ signals:
/// Fired when the import window is closed
void importDone();
void scriptLocationChanged(const QString& newPath);
void svoImportRequested(const QString& url);
@ -373,7 +366,7 @@ signals:
void headURLChanged(const QString& newValue, const QString& modelName);
void bodyURLChanged(const QString& newValue, const QString& modelName);
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
void beforeAboutToQuit();
public slots:
@ -397,7 +390,7 @@ public slots:
bool askToSetAvatarUrl(const QString& url);
bool askToLoadScript(const QString& scriptFilenameOrURL);
ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true,
ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true,
bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
void scriptFinished(const QString& scriptName);
@ -413,11 +406,11 @@ public slots:
void friendsWindowClosed();
void packageModel();
void openUrl(const QUrl& url);
void updateMyAvatarTransform();
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
void setVSyncEnabled();
@ -430,14 +423,14 @@ public slots:
void aboutApp();
void showEditEntitiesHelp();
void loadSettings();
void saveSettings();
void notifyPacketVersionMismatch();
void domainConnectionDenied(const QString& reason);
void handleDomainConnectionDeniedPacket(QSharedPointer<NLPacket> packet);
void cameraMenuChanged();
void reloadResourceCaches();
@ -447,7 +440,7 @@ private slots:
void checkFPS();
void idle();
void aboutToQuit();
void handleScriptEngineLoaded(const QString& scriptFilename);
void handleScriptLoadError(const QString& scriptFilename);
@ -457,7 +450,7 @@ private slots:
void setFullscreen(bool fullscreen);
void setEnable3DTVMode(bool enable3DTVMode);
void setEnableVRMode(bool enableVRMode);
void rotationModeChanged();
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
@ -467,9 +460,9 @@ private slots:
void shrinkMirrorView();
void manageRunningScriptsWidgetVisibility(bool shown);
void runTests();
void audioMuteToggled();
void faceTrackerMuteToggled();
@ -484,7 +477,7 @@ private:
void initDisplay();
void init();
void cleanupBeforeQuit();
void emptyLocalCache();
@ -504,7 +497,7 @@ private:
void renderLookatIndicator(glm::vec3 pointOfInterest);
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
void queryOctree(NodeType_t serverType, PacketType::Value packetType, NodeToJurisdictionMap& jurisdictions);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
glm::vec3 getSunDirection();
@ -523,8 +516,6 @@ private:
ToolWindow* _toolWindow;
WebWindowClass* _friendsWindow;
DatagramProcessor* _datagramProcessor;
QUndoStack _undoStack;
UndoStackScriptingInterface _undoStackScriptingInterface;
@ -564,7 +555,7 @@ private:
Camera _myCamera; // My view onto the world
Camera _mirrorCamera; // Cammera for mirror view
QRect _mirrorViewRect;
Setting::Handle<bool> _firstRun;
Setting::Handle<QString> _previousScriptLocation;
Setting::Handle<QString> _scriptsLocationHandle;
@ -611,8 +602,8 @@ private:
StDev _idleLoopStdev;
float _idleLoopMeasuredJitter;
int parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sendingNode);
void trackIncomingOctreePacket(const QByteArray& packet, const SharedNodePointer& sendingNode, bool wasStatsPacket);
int processOctreeStats(NLPacket& packet, SharedNodePointer sendingNode);
void trackIncomingOctreePacket(NLPacket& packet, SharedNodePointer sendingNode, bool wasStatsPacket);
NodeToJurisdictionMap _entityServerJurisdictions;
NodeToOctreeSceneStats _octreeServerSceneStats;
@ -646,17 +637,17 @@ private:
Bookmarks* _bookmarks;
bool _notifiedPacketVersionMismatchThisDomain;
QThread _settingsThread;
QTimer _settingsTimer;
GLCanvas* _glWidget = new GLCanvas(); // our GLCanvas has a couple extra features
GLCanvas* _glWidget{ nullptr };
void checkSkeleton();
QWidget* _fullscreenMenuWidget = new QWidget();
int _menuBarHeight;
QHash<QString, AcceptURLMethod> _acceptedExtensions;
QList<QString> _domainConnectionRefusals;

View file

@ -1,169 +0,0 @@
//
// DatagramProcessor.cpp
// interface/src
//
// Created by Stephen Birarda on 1/23/2014.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QWeakPointer>
#include <AccountManager.h>
#include <PerfStat.h>
#include "Application.h"
#include "avatar/AvatarManager.h"
#include "AudioClient.h"
#include "Menu.h"
#include "InterfaceLogging.h"
#include "DatagramProcessor.h"
DatagramProcessor::DatagramProcessor(QObject* parent) :
QObject(parent)
{
}
void DatagramProcessor::processDatagrams() {
if (_isShuttingDown) {
return; // bail early... we're shutting down.
}
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"DatagramProcessor::processDatagrams()");
HifiSockAddr senderSockAddr;
static QByteArray incomingPacket;
Application* application = Application::getInstance();
auto nodeList = DependencyManager::get<NodeList>();
while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) {
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
_inPacketCount++;
_inByteCount += incomingPacket.size();
if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
PacketType incomingType = packetTypeForPacket(incomingPacket);
// only process this packet if we have a match on the packet version
switch (incomingType) {
case PacketTypeAudioEnvironment:
case PacketTypeAudioStreamStats:
case PacketTypeMixedAudio:
case PacketTypeSilentAudioFrame: {
if (incomingType == PacketTypeAudioStreamStats) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioStreamStatsPacket",
Qt::QueuedConnection,
Q_ARG(QByteArray, incomingPacket));
} else if (incomingType == PacketTypeAudioEnvironment) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioEnvironmentData",
Qt::QueuedConnection,
Q_ARG(QByteArray, incomingPacket));
} else {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "addReceivedAudioToStream",
Qt::QueuedConnection,
Q_ARG(QByteArray, incomingPacket));
}
// update having heard from the audio-mixer and record the bytes received
SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket);
if (audioMixer) {
audioMixer->setLastHeardMicrostamp(usecTimestampNow());
}
break;
}
case PacketTypeEntityData:
case PacketTypeEntityErase:
case PacketTypeOctreeStats:
case PacketTypeEnvironmentData: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()... _octreeProcessor.queueReceivedPacket()");
SharedNodePointer matchedNode = DependencyManager::get<NodeList>()->sendingNodeForPacket(incomingPacket);
if (matchedNode) {
// add this packet to our list of octree packets and process them on the octree data processing
application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket);
}
break;
}
case PacketTypeBulkAvatarData:
case PacketTypeKillAvatar:
case PacketTypeAvatarIdentity:
case PacketTypeAvatarBillboard: {
// update having heard from the avatar-mixer and record the bytes received
SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket);
if (avatarMixer) {
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
QMetaObject::invokeMethod(DependencyManager::get<AvatarManager>().data(), "processAvatarMixerDatagram",
Q_ARG(const QByteArray&, incomingPacket),
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
}
break;
}
case PacketTypeDomainConnectionDenied: {
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainConnectionDenied);
QDataStream packetStream(QByteArray(incomingPacket.constData() + headerSize,
incomingPacket.size() - headerSize));
QString reason;
packetStream >> reason;
// output to the log so the user knows they got a denied connection request
// and check and signal for an access token so that we can make sure they are logged in
qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason;
qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature.";
application->domainConnectionDenied(reason);
AccountManager::getInstance().checkAndSignalForAccessToken();
break;
}
case PacketTypeNoisyMute:
case PacketTypeMuteEnvironment: {
bool mute = !DependencyManager::get<AudioClient>()->isMuted();
if (incomingType == PacketTypeMuteEnvironment) {
glm::vec3 position;
float radius;
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
float distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(),
position);
mute = mute && (distance < radius);
}
if (mute) {
DependencyManager::get<AudioClient>()->toggleMute();
if (incomingType == PacketTypeMuteEnvironment) {
AudioScriptingInterface::getInstance().environmentMuted();
} else {
AudioScriptingInterface::getInstance().mutedByMixer();
}
}
break;
}
case PacketTypeEntityEditNack:
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
application->_entityEditSender.processNackPacket(incomingPacket);
}
break;
default:
nodeList->processNodeData(senderSockAddr, incomingPacket);
break;
}
}
}
}

View file

@ -1,41 +0,0 @@
//
// DatagramProcessor.h
// interface/src
//
// Created by Stephen Birarda on 1/23/2014.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DatagramProcessor_h
#define hifi_DatagramProcessor_h
#include <QtCore/QObject>
class DatagramProcessor : public QObject {
Q_OBJECT
public:
DatagramProcessor(QObject* parent = 0);
int getInPacketCount() const { return _inPacketCount; }
int getOutPacketCount() const { return _outPacketCount; }
int getInByteCount() const { return _inByteCount; }
int getOutByteCount() const { return _outByteCount; }
void resetCounters() { _inPacketCount = 0; _outPacketCount = 0; _inByteCount = 0; _outByteCount = 0; }
void shutdown() { _isShuttingDown = true; }
public slots:
void processDatagrams();
private:
int _inPacketCount = 0;
int _outPacketCount = 0;
int _inByteCount = 0;
int _outByteCount = 0;
bool _isShuttingDown = false;
};
#endif // hifi_DatagramProcessor_h

View file

@ -13,6 +13,7 @@
#define hifi_GLCanvas_h
#include <QDebug>
#include <gpu/GPUConfig.h>
#include <QGLWidget>
#include <QTimer>

View file

@ -9,9 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// include this before QGLWidget, which includes an earlier version of OpenGL
#include "InterfaceConfig.h"
#include <QFileDialog>
#include <QMenuBar>
#include <QShortcut>
@ -391,7 +388,6 @@ Menu::Menu() {
0, // QML Qt::Key_Asterisk,
true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
0, // QML Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
@ -494,7 +490,9 @@ Menu::Menu() {
#endif
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false,
qApp->getEntityEditPacketSender(),
SLOT(toggleNackPackets()));
addCheckableActionToQMenuAndActionHash(networkMenu,
MenuOption::DisableActivityLogger,
0,

View file

@ -169,7 +169,7 @@ namespace MenuOption {
const QString DeleteBookmark = "Delete Bookmark...";
const QString DisableActivityLogger = "Disable Activity Logger";
const QString DisableLightEntities = "Disable Light Entities";
const QString DisableNackPackets = "Disable NACK Packets";
const QString DisableNackPackets = "Disable Entity NACK Packets";
const QString DiskCacheEditor = "Disk Cache Editor";
const QString DisplayHands = "Show Hand Info";
const QString DisplayHandTargets = "Show Hand Targets";
@ -290,7 +290,6 @@ namespace MenuOption {
const QString VisibleToEveryone = "Everyone";
const QString VisibleToFriends = "Friends";
const QString VisibleToNoOne = "No one";
const QString Wireframe = "Wireframe";
}
#endif // hifi_Menu_h

View file

@ -11,39 +11,173 @@
#include "Stars.h"
#include <mutex>
#include <QElapsedTimer>
#include <gpu/Batch.h>
#include <gpu/Context.h>
#include <NumericalConstants.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <TextureCache.h>
#include <RenderArgs.h>
#include <ViewFrustum.h>
#include "InterfaceConfig.h"
#include "starfield/Controller.h"
#include "../../libraries/render-utils/standardTransformPNTC_vert.h"
#include "../../libraries/render-utils/stars_frag.h"
Stars::Stars() :
_controller(0l), _starsLoaded(false) {
_controller = new starfield::Controller;
//static const float TILT = 0.23f;
static const float TILT = 0.0f;
static const unsigned int STARFIELD_NUM_STARS = 50000;
static const unsigned int STARFIELD_SEED = 1;
static const float STAR_COLORIZATION = 0.1f;
static const float TAU = 6.28318530717958f;
static const float HALF_TAU = TAU / 2.0f;
static const float QUARTER_TAU = TAU / 4.0f;
static const float MILKY_WAY_WIDTH = TAU / 30.0f; // width in radians of one half of the Milky Way
static const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees
static const float MILKY_WAY_RATIO = 0.4f;
static const char* UNIFORM_TIME_NAME = "iGlobalTime";
Stars::Stars() {
}
Stars::~Stars() {
delete _controller;
}
bool Stars::generate(unsigned numStars, unsigned seed) {
_starsLoaded = _controller->computeStars(numStars, seed);
return _starsLoaded;
// Produce a random float value between 0 and 1
static float frand() {
return (float)rand() / (float)RAND_MAX;
}
bool Stars::setResolution(unsigned k) {
return _controller->setResolution(k);
// Produce a random radian value between 0 and 2 PI (TAU)
static float rrand() {
return frand() * TAU;
}
void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
// determine length of screen diagonal from quadrant height and aspect ratio
float quadrantHeight = nearZ * tanf(RADIANS_PER_DEGREE * fovY * 0.5f);
float halfDiagonal = sqrt(quadrantHeight * quadrantHeight * (1.0f + aspect * aspect));
// determine fov angle in respect to the diagonal
float fovDiagonal = atanf(halfDiagonal / nearZ) * 2.0f;
// pull the modelview matrix off the GL stack
glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view));
_controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha);
// http://mathworld.wolfram.com/SpherePointPicking.html
static vec2 randPolar() {
vec2 result(frand(), frand());
result.x *= TAU;
result.y = powf(result.y, 2.0) / 2.0f;
if (frand() > 0.5f) {
result.y = 0.5f - result.y;
} else {
result.y += 0.5f;
}
result.y = acos((2.0f * result.y) - 1.0f);
return result;
}
static vec3 fromPolar(const vec2& polar) {
float sinTheta = sin(polar.x);
float cosTheta = cos(polar.x);
float sinPhi = sin(polar.y);
float cosPhi = cos(polar.y);
return vec3(
cosTheta * sinPhi,
cosPhi,
sinTheta * sinPhi);
}
// computeStarColor
// - Generate a star color.
//
// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is.
//
// 0 = completely black & white
// 1 = very colorful
unsigned computeStarColor(float colorization) {
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);
}
// FIXME star colors
void Stars::render(RenderArgs* renderArgs, float alpha) {
static gpu::BufferPointer vertexBuffer;
static gpu::Stream::FormatPointer streamFormat;
static gpu::Element positionElement, colorElement;
static gpu::PipelinePointer _pipeline;
static int32_t _timeSlot{ -1 };
static std::once_flag once;
const int VERTICES_SLOT = 0;
const int COLOR_SLOT = 2;
std::call_once(once, [&] {
QElapsedTimer startTime;
startTime.start();
vertexBuffer.reset(new gpu::Buffer);
srand(STARFIELD_SEED);
unsigned limit = STARFIELD_NUM_STARS;
std::vector<vec3> points;
points.resize(limit);
for (size_t star = 0; star < limit; ++star) {
points[star] = fromPolar(randPolar());
//auto color = computeStarColor(STAR_COLORIZATION);
//vertexBuffer->append(sizeof(color), (const gpu::Byte*)&color);
}
vertexBuffer->append(sizeof(vec3) * limit, (const gpu::Byte*)&points[0]);
streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
qDebug() << "Total time to generate stars: " << timeDiff << " msec";
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(stars_frag)));
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
gpu::Shader::makeProgram((*program));
_timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
_timeSlot = program->getUniforms().findLocation(UNIFORM_TIME_NAME);
}
auto state = gpu::StatePointer(new gpu::State());
// enable decal blend
state->setDepthTest(gpu::State::DepthTest(false));
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_pipeline.reset(gpu::Pipeline::create(program, state));
});
auto geometryCache = DependencyManager::get<GeometryCache>();
auto textureCache = DependencyManager::get<TextureCache>();
gpu::Batch batch;
batch.setViewTransform(Transform());
batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection());
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) *
quat(vec3(TILT, 0, 0))));
batch.setResourceTexture(0, textureCache->getWhiteTexture());
// Render the world lines
batch.setPipeline(_pipeline);
static auto start = usecTimestampNow();
float msecs = (float)(usecTimestampNow() - start) / (float)USECS_PER_MSEC;
float secs = msecs / (float)MSECS_PER_SECOND;
batch._glUniform1f(_timeSlot, secs);
geometryCache->renderUnitCube(batch);
// Render the stars
geometryCache->useSimpleDrawPipeline(batch);
batch.setInputFormat(streamFormat);
batch.setInputBuffer(VERTICES_SLOT, gpu::BufferView(vertexBuffer, positionElement));
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
renderArgs->_context->render(batch);
}

View file

@ -12,9 +12,7 @@
#ifndef hifi_Stars_h
#define hifi_Stars_h
#include <glm/glm.hpp>
namespace starfield { class Controller; }
class RenderArgs;
// Starfield rendering component.
class Stars {
@ -22,33 +20,13 @@ public:
Stars();
~Stars();
// Generate stars from random number
// The numStars parameter sets the number of stars to generate.
bool generate(unsigned numStars, unsigned seed);
// Renders the starfield from a local viewer's perspective.
// The parameters specifiy the field of view.
void render(float fovY, float aspect, float nearZ, float alpha);
// Sets the resolution for FOV culling.
//
// The parameter determines the number of tiles in azimuthal
// and altitudinal directions.
//
// GPU resources are updated upon change in which case 'true'
// is returned.
bool setResolution(unsigned k);
// Returns true when stars have been loaded
bool isStarsLoaded() const { return _starsLoaded; };
void render(RenderArgs* args, float alpha);
private:
// don't copy/assign
Stars(Stars const&); // = delete;
Stars& operator=(Stars const&); // delete;
starfield::Controller* _controller;
bool _starsLoaded;
};

View file

@ -24,7 +24,6 @@
#include <ByteCountCoding.h>
#include <SharedUtil.h>
#include "InterfaceConfig.h"
#include "world.h"
#include "Application.h"
#include "InterfaceLogging.h"

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <gpu/GPUConfig.h>
#include <limits>
#include <AudioClient.h>

View file

@ -11,8 +11,6 @@
#include <vector>
#include <gpu/GPUConfig.h>
#include <QDesktopWidget>
#include <QWindow>
@ -27,7 +25,7 @@
#include <LODManager.h>
#include <NodeList.h>
#include <NumericalConstants.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <SharedUtil.h>
@ -992,7 +990,7 @@ void Avatar::setBillboard(const QByteArray& billboard) {
_billboardTexture.reset();
}
int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) {
int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
if (!_initialized) {
// now that we have data for this Avatar we are go for init
init();
@ -1001,7 +999,7 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) {
// change in position implies movement
glm::vec3 oldPosition = _position;
int bytesRead = AvatarData::parseDataAtOffset(packet, offset);
int bytesRead = AvatarData::parseDataFromBuffer(buffer);
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
@ -1127,7 +1125,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
}
}
// virtual
// virtual
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight());

View file

@ -24,7 +24,6 @@
#include "Hand.h"
#include "Head.h"
#include "InterfaceConfig.h"
#include "SkeletonModel.h"
#include "world.h"
@ -85,10 +84,10 @@ public:
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition,
bool postLighting = false);
bool addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
bool addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
void removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
void removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
//setters
@ -146,7 +145,7 @@ public:
void setShowDisplayName(bool showDisplayName);
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
virtual int parseDataFromBuffer(const QByteArray& buffer);
static void renderJointConnectingCone( gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2,
float radius1, float radius2, const glm::vec4& color);
@ -163,7 +162,7 @@ public:
Q_INVOKABLE glm::quat getJointCombinedRotation(const QString& name) const;
Q_INVOKABLE void setJointModelPositionAndOrientation(int index, const glm::vec3 position, const glm::quat& rotation);
Q_INVOKABLE void setJointModelPositionAndOrientation(const QString& name, const glm::vec3 position,
Q_INVOKABLE void setJointModelPositionAndOrientation(const QString& name, const glm::vec3 position,
const glm::quat& rotation);
Q_INVOKABLE glm::vec3 getNeckPosition() const;
@ -179,8 +178,8 @@ public:
void slamPosition(const glm::vec3& position);
// Call this when updating Avatar position with a delta. This will allow us to
// _accurately_ measure position changes and compute the resulting velocity
// Call this when updating Avatar position with a delta. This will allow us to
// _accurately_ measure position changes and compute the resulting velocity
// (otherwise floating point error will cause problems at large positions).
void applyPositionDelta(const glm::vec3& delta);
@ -203,9 +202,9 @@ protected:
// These position histories and derivatives are in the world-frame.
// The derivatives are the MEASURED results of all external and internal forces
// and are therefore READ-ONLY --> motion control of the Avatar is NOT obtained
// and are therefore READ-ONLY --> motion control of the Avatar is NOT obtained
// by setting these values.
// Floating point error prevents us from accurately measuring velocity using a naive approach
// Floating point error prevents us from accurately measuring velocity using a naive approach
// (e.g. vel = (pos - lastPos)/dt) so instead we use _positionDeltaAccumulator.
glm::vec3 _positionDeltaAccumulator;
glm::vec3 _lastVelocity;

View file

@ -61,10 +61,17 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) {
}
AvatarManager::AvatarManager(QObject* parent) :
_avatarFades() {
_avatarFades()
{
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
_myAvatar = std::make_shared<MyAvatar>();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar");
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket");
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "processAvatarBillboardPacket");
}
void AvatarManager::init() {

View file

@ -29,7 +29,6 @@ class AvatarManager : public AvatarHashMap {
SINGLETON_DEPENDENCY
public:
/// Registers the script types associated with the avatar manager.
static void registerMetaTypes(QScriptEngine* engine);

View file

@ -8,8 +8,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <gpu/GPUConfig.h> // hack to get windows to build
#include <QImage>
#include <GeometryUtil.h>

View file

@ -11,8 +11,6 @@
#ifndef hifi_Hand_h
#define hifi_Hand_h
#include "InterfaceConfig.h"
#include <vector>
#include <QAction>
@ -30,6 +28,9 @@
class Avatar;
const float HAND_PADDLE_OFFSET = 0.1f;
const float HAND_PADDLE_THICKNESS = 0.01f;
const float HAND_PADDLE_RADIUS = 0.15f;
class Hand : public HandData {
public:

View file

@ -9,7 +9,6 @@
//
#include <glm/gtx/quaternion.hpp>
#include <gpu/GPUConfig.h>
#include <gpu/Batch.h>
#include <DependencyManager.h>

View file

@ -19,7 +19,6 @@
#include <HeadData.h>
#include "FaceModel.h"
#include "InterfaceConfig.h"
#include "world.h"

View file

@ -19,7 +19,6 @@
#include <QtCore/QTimer>
#include <gpu/GPUConfig.h>
#include <AccountManager.h>
#include <AddressManager.h>
#include <AnimationHandle.h>
@ -27,7 +26,7 @@
#include <DependencyManager.h>
#include <GeometryUtil.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <udt/PacketHeaders.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <ShapeCollider.h>
@ -851,18 +850,16 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString&
return attachment;
}
int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) {
int MyAvatar::parseDataFromBuffer(const QByteArray& buffer) {
qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar"
<< " packetLength = " << packet.size()
<< " offset = " << offset;
<< " packetLength = " << buffer.size();
// this packet is just bad, so we pretend that we unpacked it ALL
return packet.size() - offset;
return buffer.size();
}
void MyAvatar::sendKillAvatar() {
auto nodeList = DependencyManager::get<NodeList>();
QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
auto killPacket = NLPacket::create(PacketType::KillAvatar, 0);
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::AvatarMixer);
}
void MyAvatar::updateLookAtTargetAvatar() {
@ -1413,7 +1410,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
}
}
}
float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);

View file

@ -102,7 +102,7 @@ public:
eyeContactTarget getEyeContactTarget();
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
virtual int parseDataFromBuffer(const QByteArray& buffer);
static void sendKillAvatar();

View file

@ -118,7 +118,7 @@ static const float DDE_COEFFICIENT_SCALES[] = {
1.0f // CheekSquint_R
};
struct Packet {
struct DDEPacket {
//roughly in mm
float focal_length[1];
float translation[3];
@ -347,7 +347,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
bool isFiltering = Menu::getInstance()->isOptionChecked(MenuOption::VelocityFilter);
Packet packet;
DDEPacket packet;
int bytesToCopy = glm::min((int)sizeof(packet), buffer.size());
memset(&packet.name, '\n', MAX_NAME_SIZE + 1);
memcpy(&packet, buffer.data(), bytesToCopy);

View file

@ -10,16 +10,16 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InterfaceConfig.h"
#include "OculusManager.h"
#include "ui/overlays/Text3DOverlay.h"
#include <gpu/GPUConfig.h>
#include <CursorManager.h>
#include <QDesktopWidget>
#include <QGuiApplication>
#include <QScreen>
#include <QOpenGLTimerQuery>
#include <QGLWidget>
#include <CursorManager.h>
#include <glm/glm.hpp>
#include <avatar/AvatarManager.h>
@ -35,6 +35,7 @@
#include "InterfaceLogging.h"
#include "Application.h"
#include "ui/overlays/Text3DOverlay.h"
template <typename Function>
void for_each_eye(Function function) {
@ -647,12 +648,6 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFBO));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glm::quat orientation;
glm::vec3 trackerPosition;
auto deviceSize = qApp->getDeviceSize();
@ -696,12 +691,6 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
configureCamera(*_camera);
_camera->update(1.0f / Application::getInstance()->getFps());
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(glm::value_ptr(_camera->getProjection()));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
ovrRecti & vp = _eyeViewports[eye];
vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
@ -714,15 +703,10 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
});
_activeEye = ovrEye_Count;
glPopMatrix();
gpu::FramebufferPointer finalFbo;
finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
// restore our normal viewport
glViewport(0, 0, deviceSize.width(), deviceSize.height());

View file

@ -13,14 +13,16 @@
#ifndef hifi_OculusManager_h
#define hifi_OculusManager_h
#include <ProgramObject.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <QSize>
#include "RenderArgs.h"
class QOpenGLContext;
class QGLWidget;
class Camera;
/// Handles interaction with the Oculus Rift.

View file

@ -13,6 +13,7 @@
#include <avatar/AvatarManager.h>
#include <PerfStat.h>
#include <NumericalConstants.h>
#include "Application.h"
#include "SixenseManager.h"
@ -527,8 +528,8 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection();
// Get the angles, scaled between (-0.5,0.5)
float xAngle = (atan2(direction.z, direction.x) + M_PI_2);
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
float xAngle = (atan2(direction.z, direction.x) + PI_OVER_TWO);
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));
auto canvasSize = qApp->getCanvasSize();
// Get the pixel range over which the xAngle and yAngle are scaled
float cursorRange = canvasSize.x * getCursorPixelRangeMult();

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InterfaceConfig.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
@ -74,10 +72,6 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre
setFrustum(whichCamera);
glViewport (0, 0, _screenWidth, _screenHeight); // sets drawing viewport
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
@ -105,7 +99,6 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
eyeCamera.setPosition(whichCamera.getPosition());
glEnable(GL_SCISSOR_TEST);
glPushMatrix();
forEachEye([&](eyeFrustum& eye){
_activeEye = &eye;
glViewport(portalX, portalY, portalW, portalH);
@ -115,14 +108,7 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
glm::mat4 projection = glm::frustum<float>(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ);
projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0));
eyeCamera.setProjection(projection);
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); // reset projection matrix
glLoadMatrixf(glm::value_ptr(projection));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
renderArgs->_renderSide = RenderArgs::MONO;
qApp->displaySide(renderArgs, eyeCamera, false);
qApp->getApplicationCompositor().displayOverlayTexture(renderArgs);
_activeEye = NULL;
@ -130,7 +116,6 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
// render right side view
portalX = deviceSize.width() / 2;
});
glPopMatrix();
glDisable(GL_SCISSOR_TEST);
// FIXME - glow effect is removed, 3D TV mode broken until we get display plugins working

View file

@ -1,4 +1,4 @@
//Merge branch 'master' of ssh://github.com/highfidelity/hifi into isentropic/
//
// OctreePacketProcessor.cpp
// interface/src/octree
//
@ -16,91 +16,98 @@
#include "OctreePacketProcessor.h"
#include "SceneScriptingInterface.h"
void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
OctreePacketProcessor::OctreePacketProcessor() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
QSet<PacketType::Value> types {
PacketType::OctreeStats, PacketType::EntityData,
PacketType::EntityErase, PacketType::OctreeStats
};
packetReceiver.registerDirectListenerForTypes(types, this, "handleOctreePacket");
}
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
queueReceivedPacket(packet, senderNode);
}
void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"OctreePacketProcessor::processPacket()");
QByteArray mutablePacket = packet;
const int WAY_BEHIND = 300;
if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
}
int messageLength = mutablePacket.size();
Application* app = Application::getInstance();
bool wasStatsPacket = false;
PacketType::Value octreePacketType = packet->getType();
PacketType voxelPacketType = packetTypeForPacket(mutablePacket);
// note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first
// then process any remaining bytes as if it was another packet
if (voxelPacketType == PacketTypeOctreeStats) {
int statsMessageLength = app->parseOctreeStats(mutablePacket, sendingNode);
if (octreePacketType == PacketType::OctreeStats) {
int statsMessageLength = app->processOctreeStats(*packet, sendingNode);
wasStatsPacket = true;
if (messageLength > statsMessageLength) {
mutablePacket = mutablePacket.mid(statsMessageLength);
int piggybackBytes = packet->getPayloadSize() - statsMessageLength;
if (piggybackBytes) {
// construct a new packet from the piggybacked one
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[piggybackBytes]);
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggybackBytes);
// TODO: this does not look correct, the goal is to test the packet version for the piggyback, but
// this is testing the version and hash of the original packet
if (!DependencyManager::get<NodeList>()->packetVersionAndHashMatch(packet)) {
return; // bail since piggyback data doesn't match our versioning
}
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, packet->getSenderSockAddr());
packet = QSharedPointer<NLPacket>(newPacket.release());
} else {
// Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket()
return; // bail since no piggyback data
}
} // fall through to piggyback message
voxelPacketType = packetTypeForPacket(mutablePacket);
PacketVersion packetVersion = mutablePacket[1];
PacketVersion expectedVersion = versionForPacketType(voxelPacketType);
PacketType::Value packetType = packet->getType();
// check version of piggyback packet against expected version
if (packetVersion != expectedVersion) {
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
QUuid senderUUID = uuidFromPacketHeader(packet);
if (!versionDebugSuppressMap.contains(senderUUID, voxelPacketType)) {
qDebug() << "Packet version mismatch on" << voxelPacketType << "- Sender"
<< senderUUID << "sent" << (int)packetVersion << "but"
<< (int)expectedVersion << "expected.";
if (packet->getVersion() != versionForPacketType(packet->getType())) {
static QMultiMap<QUuid, PacketType::Value> versionDebugSuppressMap;
const QUuid& senderUUID = packet->getSourceID();
if (!versionDebugSuppressMap.contains(senderUUID, packetType)) {
qDebug() << "Packet version mismatch on" << packetType << "- Sender"
<< senderUUID << "sent" << (int) packetType << "but"
<< (int) versionForPacketType(packetType) << "expected.";
emit packetVersionMismatch();
versionDebugSuppressMap.insert(senderUUID, voxelPacketType);
versionDebugSuppressMap.insert(senderUUID, packetType);
}
return; // bail since piggyback version doesn't match
}
app->trackIncomingOctreePacket(*packet, sendingNode, wasStatsPacket);
app->trackIncomingOctreePacket(mutablePacket, sendingNode, wasStatsPacket);
// seek back to beginning of packet after tracking
packet->seek(0);
if (sendingNode) {
switch(packetType) {
case PacketType::EntityErase: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
app->_entities.processEraseMessage(*packet, sendingNode);
}
} break;
switch(voxelPacketType) {
case PacketTypeEntityErase: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
app->_entities.processEraseMessage(mutablePacket, sendingNode);
}
} break;
case PacketType::EntityData: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
app->_entities.processDatagram(*packet, sendingNode);
}
} break;
case PacketTypeEntityData: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
app->_entities.processDatagram(mutablePacket, sendingNode);
}
} break;
case PacketTypeEnvironmentData: {
app->_environment.parseData(*sendingNode->getActiveSocket(), mutablePacket);
} break;
default: {
// nothing to do
} break;
}
default: {
// nothing to do
} break;
}
}

View file

@ -14,15 +14,20 @@
#include <ReceivedPacketProcessor.h>
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
/// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
class OctreePacketProcessor : public ReceivedPacketProcessor {
Q_OBJECT
public:
OctreePacketProcessor();
signals:
void packetVersionMismatch();
protected:
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
private slots:
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
};
#endif // hifi_OctreePacketProcessor_h

View file

@ -1,62 +0,0 @@
//
// Config.h
// interface/src/starfield
//
// Created by Tobias Schwinger on 3/29/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Config_h
#define hifi_Config_h
#include "InterfaceConfig.h"
#include <cstddef>
#include <cfloat>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cctype>
#include <stdint.h>
#include <new>
#include <vector>
#include <memory>
#include <algorithm>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <ProgramObject.h>
#include "AngleUtil.h"
#include "Radix2InplaceSort.h"
#include "Radix2IntegerScanner.h"
#include "FloodFill.h"
// Namespace configuration:
namespace starfield {
using glm::vec3;
using glm::vec4;
using glm::dot;
using glm::normalize;
using glm::mat4;
using glm::row;
using namespace std;
typedef uint32_t nuint;
typedef quint64 wuint;
}
#endif // hifi_Config_h

View file

@ -1,70 +0,0 @@
//
// Controller.cpp
// interface/src/starfield
//
// Created by Chris Barnard on 10/16/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QElapsedTimer>
#include "InterfaceLogging.h"
#include "starfield/Controller.h"
using namespace starfield;
bool Controller::computeStars(unsigned numStars, unsigned seed) {
QElapsedTimer startTime;
startTime.start();
Generator::computeStarPositions(_inputSequence, numStars, seed);
this->retile(numStars, _tileResolution);
double NSEC_TO_MSEC = 1.0 / 1000000.0;
double timeDiff = (double)startTime.nsecsElapsed() * NSEC_TO_MSEC;
qCDebug(interfaceapp) << "Total time to retile and generate stars: " << timeDiff << "msec";
return true;
}
bool Controller::setResolution(unsigned tileResolution) {
if (tileResolution <= 3) {
return false;
}
if (tileResolution != _tileResolution) {
this->retile(_numStars, tileResolution);
return true;
} else {
return false;
}
}
void Controller::render(float perspective, float angle, mat4 const& orientation, float alpha) {
Renderer* renderer = _renderer;
if (renderer != 0l) {
renderer->render(perspective, angle, orientation, alpha);
}
}
void Controller::retile(unsigned numStars, unsigned tileResolution) {
Tiling tiling(tileResolution);
VertexOrder scanner(tiling);
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
recreateRenderer(numStars, tileResolution);
_tileResolution = tileResolution;
}
void Controller::recreateRenderer(unsigned numStars, unsigned tileResolution) {
delete _renderer;
_renderer = new Renderer(_inputSequence, numStars, tileResolution);
}

View file

@ -1,44 +0,0 @@
//
// Controller.h
// interface/src/starfield
//
// Created by Tobias Schwinger on 3/29/13.
// Modified by Chris Barnard on 10/16/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Controller_h
#define hifi_Controller_h
#include <time.h>
#include "starfield/Generator.h"
#include "starfield/data/InputVertex.h"
#include "starfield/renderer/Renderer.h"
#include "starfield/renderer/VertexOrder.h"
namespace starfield {
class Controller {
public:
Controller() : _tileResolution(20), _renderer(0l) { }
~Controller() { delete _renderer; }
bool computeStars(unsigned numStars, unsigned seed);
bool setResolution(unsigned tileResolution);
void render(float perspective, float angle, mat4 const& orientation, float alpha);
private:
void retile(unsigned numStars, unsigned tileResolution);
void recreateRenderer(unsigned numStars, unsigned tileResolution);
InputVertices _inputSequence;
unsigned _tileResolution;
unsigned _numStars;
Renderer* _renderer;
};
}
#endif // hifi_Controller_h

View file

@ -1,94 +0,0 @@
//
// Generator.cpp
// interface/src/starfield
//
// Created by Chris Barnard on 10/13/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QElapsedTimer>
#include "starfield/Generator.h"
using namespace starfield;
const float Generator::STAR_COLORIZATION = 0.1f;
const float PI_OVER_180 = 3.14159265358979f / 180.0f;
void Generator::computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed) {
InputVertices* vertices = & destination;
//_limit = limit;
QElapsedTimer startTime;
startTime.start();
srand(seed);
vertices->clear();
vertices->reserve(limit);
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.4f;
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 = powf(randFloat()*0.5f, 2.0f)/0.25f * MILKY_WAY_WIDTH;
if (randFloat() > 0.5f) {
altitude *= -1.0f;
}
// 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.
// An improvement would be to convert all stars to cartesian at this point and not have to convert back.
float tempX = sin(azimuth * PI_OVER_180) * cos(altitude * PI_OVER_180);
float tempY = sin(altitude * PI_OVER_180);
float tempZ = -cos(azimuth * PI_OVER_180) * cos(altitude * PI_OVER_180);
float xangle = MILKY_WAY_INCLINATION * PI_OVER_180;
float newX = (tempX * cos(xangle)) - (tempY * sin(xangle));
float newY = (tempX * sin(xangle)) + (tempY * cos(xangle));
float newZ = tempZ;
azimuth = (atan2(newX,-newZ) + Radians::pi()) / PI_OVER_180;
altitude = atan2(-newY, hypotf(newX, newZ)) / PI_OVER_180;
vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION)));
}
double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
qDebug() << "Total time to generate stars: " << timeDiff << " msec";
}
// computeStarColor
// - Generate a star color.
//
// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is.
//
// 0 = completely black & white
// 1 = very colorful
unsigned Generator::computeStarColor(float colorization) {
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);
}

View file

@ -1,40 +0,0 @@
//
// Generator.h
// interface/src/starfield
//
// Created by Chris Barnard on 10/13/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Generator_h
#define hifi_Generator_h
#include <locale.h>
#include <time.h>
#include "Config.h"
#include "SharedUtil.h"
#include "starfield/data/InputVertex.h"
namespace starfield {
class Generator {
public:
Generator() {}
~Generator() {}
static void computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed);
static unsigned computeStarColor(float colorization);
private:
static const float STAR_COLORIZATION;
};
}
#endif // hifi_Generator_h

View file

@ -1,26 +0,0 @@
//
// GpuVertex.cpp
// interface/src/starfield/data
//
// Created by Chris Barnard on 10/17/13.
// Based on code by Tobias Schwinger on 3/29/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "starfield/data/GpuVertex.h"
using namespace starfield;
GpuVertex::GpuVertex(InputVertex const& inputVertex) {
_color = inputVertex.getColor();
float azimuth = inputVertex.getAzimuth();
float altitude = inputVertex.getAltitude();
// compute altitude/azimuth into X/Y/Z point on a sphere
_valX = sin(azimuth) * cos(altitude);
_valY = sin(altitude);
_valZ = -cos(azimuth) * cos(altitude);
}

View file

@ -1,37 +0,0 @@
//
// GpuVertex.h
// interface/src/starfield/data
//
// Created by Tobias Schwinger on 3/29/13.
// Modified by Chris Barnard on 10/17/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_GpuVertex_h
#define hifi_GpuVertex_h
#include "starfield/data/InputVertex.h"
namespace starfield {
class GpuVertex {
public:
GpuVertex() { }
GpuVertex(InputVertex const& inputVertex);
unsigned getColor() const { return _color; }
private:
unsigned _color;
float _valX;
float _valY;
float _valZ;
};
}
#endif // hifi_GpuVertex_h

View file

@ -1,27 +0,0 @@
//
// InputVertex.cpp
// interface/src/starfield/data
//
// Created by Chris Barnard on 10/17/13.
// Based on code by Tobias Schwinger 3/29/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "starfield/data/InputVertex.h"
using namespace starfield;
InputVertex::InputVertex(float azimuth, float altitude, unsigned color) {
_color = color | 0xff000000u;
azimuth = angleConvert<Degrees,Radians>(azimuth);
altitude = angleConvert<Degrees,Radians>(altitude);
angleHorizontalPolar<Radians>(azimuth, altitude);
_azimuth = azimuth;
_altitude = altitude;
}

View file

@ -1,39 +0,0 @@
//
// InputVertex.h
// interface/src/starfield/data
//
// Created by Tobias Schwinger on 3/29/13.
// Modified by Chris Barnard on 10/17/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_InputVertex_h
#define hifi_InputVertex_h
#include "starfield/Config.h"
namespace starfield {
class InputVertex {
public:
InputVertex(float azimuth, float altitude, unsigned color);
float getAzimuth() const { return _azimuth; }
float getAltitude() const { return _altitude; }
unsigned getColor() const { return _color; }
private:
unsigned _color;
float _azimuth;
float _altitude;
};
typedef std::vector<InputVertex> InputVertices;
}
#endif // hifi_InputVertex_h

View file

@ -1,32 +0,0 @@
//
// Tile.h
// interface/src/starfield/data
//
// Created by Tobias Schwinger on 3/22/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Tile_h
#define hifi_Tile_h
#include "starfield/Config.h"
namespace starfield {
struct Tile {
nuint offset;
nuint count;
nuint flags;
// flags
static uint16_t const checked = 1;
static uint16_t const visited = 2;
static uint16_t const render = 4;
};
}
#endif // hifi_Tile_h

View file

@ -1,324 +0,0 @@
//
// Renderer.cpp
// interface/src/starfield/renderer
//
// Created by Chris Barnard on 10/17/13.
// Based on earlier work by Tobias Schwinger 3/22/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "starfield/renderer/Renderer.h"
#include "Application.h"
using namespace starfield;
Renderer::Renderer(InputVertices const& stars, unsigned numStars, unsigned tileResolution) : _dataArray(0l),
_tileArray(0l), _tiling(tileResolution) {
this->glAlloc();
Tiling tiling(tileResolution);
size_t numTiles = tiling.getTileCount();
// REVISIT: batch arrays are probably oversized, but - hey - they
// are not very large (unless for insane tiling) and we're better
// off safe than sorry
_dataArray = new GpuVertex[numStars];
_tileArray = new Tile[numTiles + 1];
_batchOffs = new GLint[numTiles * 2];
_batchCountArray = new GLsizei[numTiles * 2];
prepareVertexData(stars, numStars, tiling);
this->glUpload(numStars);
}
Renderer::~Renderer() {
delete[] _dataArray;
delete[] _tileArray;
delete[] _batchCountArray;
delete[] _batchOffs;
this->glFree();
}
void Renderer::render(float perspective, float aspect, mat4 const& orientation, float alpha) {
float halfPersp = perspective * 0.5f;
// cancel all translation
mat4 matrix = orientation;
matrix[3][0] = 0.0f;
matrix[3][1] = 0.0f;
matrix[3][2] = 0.0f;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadMatrixf(glm::value_ptr(qApp->getDisplayViewFrustum()->getProjection()));
glMatrixMode(GL_MODELVIEW);
// extract local z vector
vec3 ahead = vec3(matrix[2]);
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z));
angleHorizontalPolar<Radians>(azimuth, altitude);
float const eps = 0.002f;
altitude = glm::clamp(altitude, -Radians::halfPi() + eps, Radians::halfPi() - eps);
matrix = glm::affineInverse(matrix);
this->_outIndexPos = (unsigned*) _batchOffs;
this->_wRowVec = -vec3(row(matrix, 2));
this->_halfPerspectiveAngle = halfPersp;
TileSelection::Cursor cursor;
cursor.current = _tileArray + _tiling.getTileIndex(azimuth, altitude);
cursor.firstInRow = _tileArray + _tiling.getTileIndex(0.0f, altitude);
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray));
this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
// renderer construction
void Renderer::prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling) {
size_t nTiles = tiling.getTileCount();
size_t vertexIndex = 0u, currTileIndex = 0u, count_active = 0u;
_tileArray[0].offset = 0u;
_tileArray[0].flags = 0u;
for (InputVertices::const_iterator i = vertices.begin(), e = vertices.end(); i != e; ++i) {
size_t tileIndex = tiling.getTileIndex(i->getAzimuth(), i->getAltitude());
assert(tileIndex >= currTileIndex);
// moved on to another tile? -> flush
if (tileIndex != currTileIndex) {
Tile* tile = _tileArray + currTileIndex;
Tile* lastTile = _tileArray + tileIndex;
// set count of active vertices (upcoming lod)
tile->count = count_active;
// generate skipped, empty tiles
for(size_t offset = vertexIndex; ++tile != lastTile ;) {
tile->offset = offset, tile->count = 0u, tile->flags = 0u;
}
// initialize next (as far as possible here)
lastTile->offset = vertexIndex;
lastTile->flags = 0u;
currTileIndex = tileIndex;
count_active = 0u;
}
++count_active;
// write converted vertex
_dataArray[vertexIndex++] = *i;
}
assert(vertexIndex == numStars);
// flush last tile (see above)
Tile* tile = _tileArray + currTileIndex;
tile->count = count_active;
for (Tile* e = _tileArray + nTiles + 1; ++tile != e;) {
tile->offset = vertexIndex, tile->count = 0u, tile->flags = 0;
}
}
bool Renderer::visitTile(Tile* tile) {
unsigned index = tile - _tileArray;
*_outIndexPos++ = index;
return isTileVisible(index);
}
bool Renderer::isTileVisible(unsigned index) {
float slice = _tiling.getSliceAngle();
float halfSlice = 0.5f * slice;
unsigned stride = _tiling.getAzimuthalTiles();
float azimuth = (index % stride) * slice;
float altitude = (index / stride) * slice - Radians::halfPi();
float groundX = sin(azimuth);
float groundZ = -cos(azimuth);
float elevation = cos(altitude);
vec3 tileCenter = vec3(groundX * elevation, sin(altitude), groundZ * elevation);
float w = dot(_wRowVec, tileCenter);
float daz = halfSlice * cos(std::max(0.0f, abs(altitude) - halfSlice));
float dal = halfSlice;
float adjustedNear = cos(_halfPerspectiveAngle + sqrt(daz * daz + dal * dal));
return w >= adjustedNear;
}
unsigned Renderer::prepareBatch(unsigned const* indices, unsigned const* indicesEnd) {
unsigned nRanges = 0u;
GLint* offs = _batchOffs;
GLsizei* count = _batchCountArray;
for (unsigned* i = (unsigned*) _batchOffs; i != indicesEnd; ++i) {
Tile* t = _tileArray + *i;
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
*offs++ = t->offset;
*count++ = t->count;
++nRanges;
}
t->flags = 0;
}
return nRanges;
}
// GL API handling
void Renderer::glAlloc() {
GLchar const* const VERTEX_SHADER =
"#version 120\n"
"uniform float alpha;\n"
"void main(void) {\n"
" vec3 c = gl_Color.rgb * 1.22;\n"
" float s = min(max(tan((c.r + c.g + c.b) / 3), 1.0), 3.0);\n"
" gl_Position = ftransform();\n"
" gl_FrontColor= gl_Color * alpha * 1.5;\n"
" gl_PointSize = s;\n"
"}\n";
_program.addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER);
GLchar const* const FRAGMENT_SHADER =
"#version 120\n"
"void main(void) {\n"
" gl_FragColor = gl_Color;\n"
"}\n";
_program.addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER);
_program.link();
_alphaLocationHandle = _program.uniformLocation("alpha");
glGenBuffersARB(1, & _vertexArrayHandle);
}
void Renderer::glFree() {
glDeleteBuffersARB(1, & _vertexArrayHandle);
}
void Renderer::glUpload(GLsizei numStars) {
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
glBufferData(GL_ARRAY_BUFFER, numStars * sizeof(GpuVertex), _dataArray, GL_STATIC_DRAW);
glBindBufferARB(GL_ARRAY_BUFFER, 0);
}
void Renderer::glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) {
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
// setup modelview matrix
glPushMatrix();
glLoadMatrixf(matrix);
// set point size and smoothing + shader control
glPointSize(1.0f);
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
// select shader and vertex array
_program.bind();
_program.setUniformValue(_alphaLocationHandle, alpha);
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
// render
glMultiDrawArrays(GL_POINTS, _batchOffs, _batchCountArray, n_ranges);
// restore state
glBindBufferARB(GL_ARRAY_BUFFER, 0);
_program.release();
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
glDisable(GL_POINT_SMOOTH);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glPopMatrix();
}
// flood fill strategy
bool Renderer::TileSelection::select(Renderer::TileSelection::Cursor const& cursor) {
Tile* tile = cursor.current;
if (tile < _tileArray || tile >= _tilesEnd || !! (tile->flags & Tile::checked)) {
// out of bounds or been here already
return false;
}
// will check now and never again
tile->flags |= Tile::checked;
if (_rendererRef.visitTile(tile)) {
// good one -> remember (for batching) and propagate
tile->flags |= Tile::render;
return true;
}
return false;
}
bool Renderer::TileSelection::process(Renderer::TileSelection::Cursor const& cursor) {
Tile* tile = cursor.current;
if (! (tile->flags & Tile::visited)) {
tile->flags |= Tile::visited;
return true;
}
return false;
}
void Renderer::TileSelection::right(Renderer::TileSelection::Cursor& cursor) const {
cursor.current += 1;
if (cursor.current == cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles()) {
cursor.current = cursor.firstInRow;
}
}
void Renderer::TileSelection::left(Renderer::TileSelection::Cursor& cursor) const {
if (cursor.current == cursor.firstInRow) {
cursor.current = cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles();
}
cursor.current -= 1;
}
void Renderer::TileSelection::up(Renderer::TileSelection::Cursor& cursor) const {
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
cursor.current += numTiles;
cursor.firstInRow += numTiles;
}
void Renderer::TileSelection::down(Renderer::TileSelection::Cursor& cursor) const {
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
cursor.current -= numTiles;
cursor.firstInRow -= numTiles;
}
void Renderer::TileSelection::defer(Renderer::TileSelection::Cursor const& cursor) {
*_stackPos++ = cursor;
}
bool Renderer::TileSelection::deferred(Renderer::TileSelection::Cursor& cursor) {
if (_stackPos != _stackArray) {
cursor = *--_stackPos;
return true;
}
return false;
}

View file

@ -1,142 +0,0 @@
//
// Renderer.h
// interface/src/starfield/renderer
//
// Created by Tobias Schwinger on 3/22/13.
// Modified by Chris Barnard on 10/17/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Renderer_h
#define hifi_Renderer_h
#include "starfield/Config.h"
#include "starfield/data/InputVertex.h"
#include "starfield/data/Tile.h"
#include "starfield/data/GpuVertex.h"
#include "starfield/renderer/Tiling.h"
//
// FOV culling
// ===========
//
// As stars can be thought of as at infinity distance, the field of view only
// depends on perspective and rotation:
//
// _----_ <-- visible stars
// from above +-near-+ - -
// \ / |
// near width: \ / | cos(p/2)
// 2sin(p/2) \/ _
// center
//
//
// Now it is important to note that a change in altitude maps uniformly to a
// distance on a sphere. This is NOT the case for azimuthal angles: In this
// case a factor of 'cos(alt)' (the orbital radius) applies:
//
//
// |<-cos alt ->| | |<-|<----->|->| d_azi cos(alt)
// |
// __--* | --------- -
// __-- * | | | ^ d_alt
// __-- alt) * | | | v
// --------------*- | ------------- -
// |
// side view | tile on sphere
//
//
// This lets us find a worst-case (Eigen) angle from the center to the edge
// of a tile as
//
// hypot( 0.5 d_alt, 0.5 d_azi cos(alt_absmin) ).
//
// This angle must be added to 'p' (the perspective angle) in order to find
// an altered near plane for the culling decision.
//
namespace starfield {
class Renderer {
public:
Renderer(InputVertices const& src, unsigned numStars, unsigned tileResolution);
~Renderer();
void render(float perspective, float aspect, mat4 const& orientation, float alpha);
private:
// renderer construction
void prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling);
// FOV culling / LOD
class TileSelection;
friend class Renderer::TileSelection;
class TileSelection {
public:
struct Cursor { Tile* current, * firstInRow; };
private:
Renderer& _rendererRef;
Cursor* const _stackArray;
Cursor* _stackPos;
Tile const* const _tileArray;
Tile const* const _tilesEnd;
public:
TileSelection(Renderer& renderer, Tile const* tiles, Tile const* tiles_end, Cursor* stack) :
_rendererRef(renderer),
_stackArray(stack),
_stackPos(stack),
_tileArray(tiles),
_tilesEnd(tiles_end) { }
protected:
bool select(Cursor const& cursor);
bool process(Cursor const& cursor);
void right(Cursor& cursor) const;
void left(Cursor& cursor) const;
void up(Cursor& cursor) const;
void down(Cursor& cursor) const;
void defer(Cursor const& cursor);
bool deferred(Cursor& cursor);
};
bool visitTile(Tile* tile);
bool isTileVisible(unsigned index);
unsigned prepareBatch(unsigned const* indices, unsigned const* indicesEnd);
// GL API handling
void glAlloc();
void glFree();
void glUpload(GLsizei numStars);
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha);
// variables
GpuVertex* _dataArray;
Tile* _tileArray;
GLint* _batchOffs;
GLsizei* _batchCountArray;
GLuint _vertexArrayHandle;
ProgramObject _program;
int _alphaLocationHandle;
Tiling _tiling;
unsigned* _outIndexPos;
vec3 _wRowVec;
float _halfPerspectiveAngle;
};
}
#endif // hifi_Renderer_h

View file

@ -1,48 +0,0 @@
//
// Tiling.h
// interface/src/starfield/renderer
//
// Created by Tobias Schwinger on 3/22/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Tiling_h
#define hifi_Tiling_h
#include "starfield/Config.h"
namespace starfield {
const float LOG2 = 1.4426950408889634f;
class Tiling {
public:
Tiling(unsigned tileResolution) : _tileResolution(tileResolution), _rcpSlice(tileResolution / Radians::twicePi()) {
_nBits = ceil(log((float)getTileCount()) * LOG2); }
unsigned getAzimuthalTiles() const { return _tileResolution; }
unsigned getAltitudinalTiles() const { return _tileResolution / 2 + 1; }
unsigned getTileIndexBits() const { return _nBits; }
unsigned getTileCount() const { return getAzimuthalTiles() * getAltitudinalTiles(); }
unsigned getTileIndex(float azimuth, float altitude) const { return discreteAzimuth(azimuth) +
_tileResolution * discreteAltitude(altitude); }
float getSliceAngle() const { return 1.0f / _rcpSlice; }
private:
unsigned discreteAngle(float unsigned_angle) const { return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f)); }
unsigned discreteAzimuth(float angle) const { return discreteAngle(angle) % _tileResolution; }
unsigned discreteAltitude(float angle) const { return min( getAltitudinalTiles() - 1,
discreteAngle(angle + Radians::halfPi()) ); }
// variables
unsigned _tileResolution;
float _rcpSlice;
unsigned _nBits;
};
}
#endif // hifi_Tiling_h

View file

@ -1,20 +0,0 @@
//
// VertexOrder.cpp
// interface/src/starfield/renderer
//
// Created by Chris Barnard on 10/17/13.
// Based on code by Tobias Schwinger on 3/22/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "starfield/renderer/VertexOrder.h"
using namespace starfield;
bool VertexOrder::bit(InputVertex const& vertex, state_type const& state) const {
unsigned key = _tiling.getTileIndex(vertex.getAzimuth(), vertex.getAltitude());
return base::bit(key, state);
}

View file

@ -1,41 +0,0 @@
//
// VertexOrder.h
// interface/src/starfield/renderer
//
// Created by Tobias Schwinger on 3/22/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_VertexOrder_h
#define hifi_VertexOrder_h
#include "starfield/Config.h"
#include "starfield/data/InputVertex.h"
#include "starfield/renderer/Tiling.h"
namespace starfield {
// Defines the vertex order for the renderer as a bit extractor for
//binary in-place Radix Sort.
class VertexOrder : public Radix2IntegerScanner<unsigned>
{
public:
explicit VertexOrder(Tiling const& tiling) :
base(tiling.getTileIndexBits()), _tiling(tiling) { }
bool bit(InputVertex const& vertex, state_type const& state) const;
private:
Tiling _tiling;
typedef Radix2IntegerScanner<unsigned> base;
};
} // anonymous namespace
#endif // hifi_VertexOrder_h

View file

@ -9,14 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InterfaceConfig.h"
#include "ApplicationCompositor.h"
#include <glm/gtc/type_ptr.hpp>
#include <avatar/AvatarManager.h>
#include <gpu/GLBackend.h>
#include <NumericalConstants.h>
#include "CursorManager.h"
#include "Tooltip.h"
@ -501,8 +500,8 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection();
// Get the angles, scaled between (-0.5,0.5)
float xAngle = (atan2(direction.z, direction.x) + M_PI_2);
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
float xAngle = (atan2(direction.z, direction.x) + PI_OVER_TWO);
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));
// Get the pixel range over which the xAngle and yAngle are scaled
float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult();

View file

@ -62,7 +62,7 @@ public:
glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
GLuint getOverlayTexture() const;
uint32_t getOverlayTexture() const;
static glm::vec2 directionToSpherical(const glm::vec3 & direction);
static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos);

View file

@ -9,11 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InterfaceConfig.h"
#include <QOpenGLFramebufferObject>
#include <QOpenGLTexture>
#include <glm/gtc/type_ptr.hpp>
#include <avatar/AvatarManager.h>
@ -54,7 +49,6 @@ ApplicationOverlay::ApplicationOverlay()
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [&](GLuint textureId) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->lockTexture(textureId);
assert(!glGetError());
std::swap(_uiTexture, textureId);
if (textureId) {
offscreenUi->releaseTexture(textureId);

View file

@ -40,7 +40,7 @@ private:
float _alpha{ 1.0f };
float _trailingAudioLoudness{ 0.0f };
GLuint _uiTexture{ 0 };
uint32_t _uiTexture{ 0 };
int _domainStatusBorder;
int _magnifierBorder;

View file

@ -9,9 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <cstdio>
#include "AudioStatsDialog.h"
#include "InterfaceConfig.h"
#include <cstdio>
#include <AudioClient.h>
#include <AudioConstants.h>
@ -22,7 +22,6 @@
#include <Util.h>
#include "AudioStatsDialog.h"
const unsigned COLOR0 = 0x33cc99ff;
const unsigned COLOR1 = 0xffef40c0;

View file

@ -1,222 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdouble-promotion"
#endif
#include <QtWidgets>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#include "FlowLayout.h"
//! [1]
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
: m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
//! [1]
//! [2]
FlowLayout::~FlowLayout()
{
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
//! [2]
//! [3]
void FlowLayout::addItem(QLayoutItem *item)
{
itemList.append(item);
}
//! [3]
//! [4]
int FlowLayout::horizontalSpacing() const
{
if (m_hSpace >= 0) {
return m_hSpace;
} else {
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
}
}
int FlowLayout::verticalSpacing() const
{
if (m_vSpace >= 0) {
return m_vSpace;
} else {
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}
}
//! [4]
//! [5]
int FlowLayout::count() const
{
return itemList.size();
}
QLayoutItem *FlowLayout::itemAt(int index) const
{
return itemList.value(index);
}
QLayoutItem *FlowLayout::takeAt(int index)
{
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
return 0;
}
//! [5]
//! [6]
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
//! [6]
//! [7]
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth(int width) const
{
int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
//! [7]
//! [8]
void FlowLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
doLayout(rect, false);
}
QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
QSize FlowLayout::minimumSize() const
{
QSize size;
QLayoutItem *item;
foreach (item, itemList)
size = size.expandedTo(item->minimumSize());
size += QSize(2*margin(), 2*margin());
return size;
}
//! [8]
//! [9]
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
//! [9]
//! [10]
QLayoutItem *item;
foreach (item, itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
//! [10]
//! [11]
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
return y + lineHeight - rect.y() + bottom;
}
//! [11]
//! [12]
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
QObject *parent = this->parent();
if (!parent) {
return -1;
} else if (parent->isWidgetType()) {
QWidget *pw = static_cast<QWidget *>(parent);
return pw->style()->pixelMetric(pm, 0, pw);
} else {
return static_cast<QLayout *>(parent)->spacing();
}
}
//! [12]

View file

@ -1,78 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H
#include <QLayout>
#include <QRect>
#include <QStyle>
//! [0]
class FlowLayout : public QLayout
{
public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
~FlowLayout();
void addItem(QLayoutItem *item);
int horizontalSpacing() const;
int verticalSpacing() const;
Qt::Orientations expandingDirections() const;
bool hasHeightForWidth() const;
int heightForWidth(int) const;
int count() const;
QLayoutItem *itemAt(int index) const;
QSize minimumSize() const;
void setGeometry(const QRect &rect);
QSize sizeHint() const;
QLayoutItem *takeAt(int index);
private:
int doLayout(const QRect &rect, bool testOnly) const;
int smartSpacing(QStyle::PixelMetric pm) const;
QList<QLayoutItem *> itemList;
int m_hSpace;
int m_vSpace;
};
//! [0]
#endif // FLOWLAYOUT_H

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InterfaceConfig.h"
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdouble-promotion"
@ -18,7 +16,6 @@
#include <QDesktopWidget>
#include <QTextBlock>
#include <QtGui>
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop

View file

@ -12,8 +12,6 @@
#ifndef hifi_LogDialog_h
#define hifi_LogDialog_h
#include "InterfaceConfig.h"
#include <QDialog>
#include <QMutex>
#include <QPlainTextEdit>

View file

@ -94,7 +94,6 @@ void OctreeStatsDialog::moreless(const QString& link) {
int OctreeStatsDialog::AddStatItem(const char* caption, unsigned colorRGBA) {
char strBuf[64];
const int STATS_LABEL_WIDTH = 600;
_statCount++; // increment our current stat count
@ -115,8 +114,7 @@ int OctreeStatsDialog::AddStatItem(const char* caption, unsigned colorRGBA) {
rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3);
palette.setColor(QPalette::WindowText, QColor::fromRgb(rgb));
label->setPalette(palette);
snprintf(strBuf, sizeof(strBuf), " %s:", caption);
_form->addRow(strBuf, label);
_form->addRow(QString(" %1:").arg(caption), label);
label->setFixedWidth(STATS_LABEL_WIDTH);
return _statCount;

View file

@ -28,7 +28,6 @@
#include <QWidget>
#include "Application.h"
#include "FlowLayout.h"
#include "JSConsole.h"
#include "PathUtils.h"

View file

@ -6,8 +6,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <gpu/GPUConfig.h>
#include "Stats.h"
#include <sstream>
@ -26,7 +24,6 @@
#include <PerfStat.h>
#include "BandwidthRecorder.h"
#include "InterfaceConfig.h"
#include "Menu.h"
#include "Util.h"
#include "SequenceNumberStats.h"

View file

@ -9,12 +9,9 @@
#ifndef hifi_Stats_h
#define hifi_Stats_h
#include <QObject>
#include <QQuickItem>
#include <QVector3D>
#include <OffscreenUi.h>
#include <OffscreenQmlElement.h>
#include <RenderArgs.h>
#include <QVector3D>
#define STATS_PROPERTY(type, name, initialValue) \
Q_PROPERTY(type name READ name NOTIFY name##Changed) \

Some files were not shown because too many files have changed in this diff Show more