mirror of
https://github.com/overte-org/overte.git
synced 2025-08-05 05:20:00 +02:00
merge from upstream
This commit is contained in:
commit
cdc26526b4
291 changed files with 6022 additions and 10079 deletions
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class AssignmentFactory {
|
||||
public:
|
||||
static ThreadedAssignment* unpackAssignment(const QByteArray& packet);
|
||||
static ThreadedAssignment* unpackAssignment(NLPacket& packet);
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentFactory_h
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.";
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(×tamp);
|
||||
|
||||
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(×tamp);
|
||||
|
||||
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(×tamp);
|
||||
|
||||
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(×tamp);
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_GLCanvas_h
|
||||
|
||||
#include <QDebug>
|
||||
#include <gpu/GPUConfig.h>
|
||||
#include <QGLWidget>
|
||||
#include <QTimer>
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <ByteCountCoding.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "world.h"
|
||||
#include "Application.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
//
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <gpu/GPUConfig.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <HeadData.h>
|
||||
|
||||
#include "FaceModel.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "world.h"
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
|
||||
eyeContactTarget getEyeContactTarget();
|
||||
|
||||
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
|
||||
static void sendKillAvatar();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -40,7 +40,7 @@ private:
|
|||
|
||||
float _alpha{ 1.0f };
|
||||
float _trailingAudioLoudness{ 0.0f };
|
||||
GLuint _uiTexture{ 0 };
|
||||
uint32_t _uiTexture{ 0 };
|
||||
|
||||
int _domainStatusBorder;
|
||||
int _magnifierBorder;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#ifndef hifi_LogDialog_h
|
||||
#define hifi_LogDialog_h
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMutex>
|
||||
#include <QPlainTextEdit>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <QWidget>
|
||||
|
||||
#include "Application.h"
|
||||
#include "FlowLayout.h"
|
||||
#include "JSConsole.h"
|
||||
#include "PathUtils.h"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue