Merge branch 'receive_packets' of https://github.com/huffman/hifi into protocol

This commit is contained in:
Atlante45 2015-07-14 16:36:28 -07:00
commit fbb6a94579
107 changed files with 1920 additions and 2049 deletions
assignment-client/src
domain-server/src
interface/src
libraries

View file

@ -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,
@ -47,94 +47,64 @@ Agent::Agent(const QByteArray& packet) :
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>();
void Agent::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
auto packetType = packet->getType();
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
PacketType::Value datagramPacketType = packetTypeForPacket(receivedPacket);
if (packetType == PacketType::OctreeStats) {
if (datagramPacketType == PacketType::Jurisdiction) {
int headerBytes = numBytesForPacketHeader(receivedPacket);
int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(packet, senderNode);
if (packet->getSizeUsed() > statsMessageLength) {
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
int piggyBackedSizeWithHeader = packet->getSizeUsed() - statsMessageLength;
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[piggyBackedSizeWithHeader]);
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader);
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 == PacketType::OctreeStats
|| datagramPacketType == PacketType::EntityData
|| datagramPacketType == PacketType::EntityErase
) {
// Make sure our Node and NodeList knows we've heard from this node.
SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket);
sourceNode->setLastHeardMicrostamp(usecTimestampNow());
QByteArray mutablePacket = receivedPacket;
int messageLength = mutablePacket.size();
if (datagramPacketType == PacketType::OctreeStats) {
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 == PacketType::EntityData || datagramPacketType == PacketType::EntityErase) {
_entityViewer.processDatagram(mutablePacket, sourceNode);
}
} else if (datagramPacketType == PacketType::MixedAudio || datagramPacketType == PacketType::SilentAudioFrame) {
_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 == PacketType::BulkAvatarData
|| datagramPacketType == PacketType::AvatarIdentity
|| datagramPacketType == PacketType::AvatarBillboard
|| datagramPacketType == PacketType::KillAvatar) {
// 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() {

View file

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

View file

@ -62,6 +62,17 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
// 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);
// 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();
@ -104,9 +115,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 +129,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 +160,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 +179,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(nodeList->getSessionUUID().toRfc4122());
statusPacket->writePrimitive(assignmentType);
nodeList->sendPacket(std::move(statusPacket), _assignmentClientMonitorSocket);
}
void AssignmentClient::sendAssignmentRequest() {
@ -206,85 +230,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) == PacketType::CreateAssignment) {
auto nodeList = DependencyManager::get<NodeList>();
qDebug() << "Received a PacketType::CreateAssignment - 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) == PacketType::StopNode) {
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
qDebug() << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketType::StopNode.";
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 +330,8 @@ void AssignmentClient::assignmentCompleted() {
auto nodeList = DependencyManager::get<NodeList>();
// have us handle incoming NodeList datagrams again, and make sure our ThreadedAssignment isn't handling them
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
// tell the packet receiver to stop dropping packets
nodeList->getPacketReceiver().setShouldDropPackets(false);
// reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NodeType::Unassigned);

View file

@ -15,30 +15,35 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QPointer>
#include <PacketListener.h>
#include "ThreadedAssignment.h"
class QSharedMemory;
class AssignmentClient : public QObject {
class AssignmentClient : public QObject, public PacketListener {
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;

View file

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

View file

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

View file

@ -53,7 +53,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,9 +173,9 @@ 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") {
if (childData->getChildType() == Assignment::Type::AllTypes) {
spareCount ++;
aSpareId = node->getUUID();
}
@ -201,63 +202,53 @@ void AssignmentClientMonitor::checkSpares() {
}
}
void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<NLPacket> packet) {
QUuid senderID = QUuid::fromRfc4122(QByteArray::fromRawData(packet->getData(), 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) == PacketType::NodeJsonStats) {
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.";
auto diePacket = NLPacket::create(PacketType::StopNode, 0);
nodeList->sendPacket(std::move(diePacket), senderSockAddr);
}
}
}
if (matchingNode) {
// update our records about how to reach this child
matchingNode->setLocalSocket(senderSockAddr);
QVariantMap packetVariantMap =
JSONBreakableMarshal::fromStringBuffer(receivedPacket.mid(numBytesForPacketHeader(receivedPacket)));
QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(packetVariantMap);
// get child's assignment type out of the decoded json
QString childType = unpackedStatsJSON["assignment_type"].toString();
AssignmentClientChildData *childData =
static_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
childData->setChildType(childType);
// note when this child talked
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
}
if (!matchingNode) {
// The parent only expects to be talking with prorams running on this same machine.
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
if (!senderID.isNull()) {
// We don't have this node yet - we should add it
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode
(senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false);
childData = new AssignmentClientChildData(Assignment::Type::AllTypes);
matchingNode->setLinkedData(childData);
} else {
// have the NodeList attempt to handle it
nodeList->processNodeData(senderSockAddr, receivedPacket);
// tell unknown assignment-client child to exit.
qDebug() << "Asking unknown child at" << senderSockAddr << "to exit.";
auto diePacket = NLPacket::create(PacketType::StopNode, 0);
nodeList->sendPacket(std::move(diePacket), senderSockAddr);
return;
}
}
} else {
childData = dynamic_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
}
if (childData) {
// update our records about how to reach this child
matchingNode->setLocalSocket(senderSockAddr);
// get child's assignment type out of the packet
quint8 assignmentType;
packet->readPrimitive(&assignmentType);
childData->setChildType((Assignment::Type) assignmentType);
// note when this child talked
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
}
}

View file

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

View file

@ -17,15 +17,13 @@
#include "avatars/AvatarMixer.h"
#include "entities/EntityServer.h"
ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) {
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
quint8 packedType;
packetStream >> packedType;
packet.peekPrimitive(&packedType);
Assignment::Type unpackedType = (Assignment::Type) packedType;
switch (unpackedType) {
case Assignment::AudioMixerType:
return new AudioMixer(packet);

View file

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

View file

@ -52,7 +52,6 @@
#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;
@ -487,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) ||
@ -532,37 +544,24 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer 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->getSizeUsed());
// Copy payload
newPacket->write(packet->getPayload(), packet->getSizeUsed());
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
// pull any new audio data from nodes off of the network stack
PacketType::Value mixerPacketType = packetTypeForPacket(receivedPacket);
if (mixerPacketType == PacketType::MicrophoneAudioNoEcho
|| mixerPacketType == PacketType::MicrophoneAudioWithEcho
|| mixerPacketType == PacketType::InjectAudio
|| mixerPacketType == PacketType::SilentAudioFrame
|| mixerPacketType == PacketType::AudioStreamStats) {
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
} else if (mixerPacketType == PacketType::MuteEnvironment) {
SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (sendingNode->getCanAdjustLocks()) {
auto packet = NLPacket::create(PacketType::MuteEnvironment);
// Copy payload
packet->write(receivedPacket.mid(numBytesForPacketHeader(receivedPacket)));
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
node->getLinkedData() && node != sendingNode) {
nodeList->sendPacket(std::move(packet), *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);
}
});
}
}
@ -656,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) {

View file

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

View file

@ -49,20 +49,18 @@ AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const {
return NULL;
}
int AudioMixerClientData::parseData(const QByteArray& packet) {
PacketType::Value packetType = packetTypeForPacket(packet);
int AudioMixerClientData::parseData(NLPacket& packet) {
PacketType::Value packetType = packet.getType();
if (packetType == PacketType::AudioStreamStats) {
const char* dataAt = packet.data();
// 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;
@ -76,8 +74,11 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
// 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()));
@ -88,20 +89,24 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
// 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;

View file

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

View file

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

View file

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

View file

@ -33,7 +33,7 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
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() {
@ -394,65 +400,44 @@ 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>();
nodeList->updateNodeWithDataFromPacket(packet, senderNode);
}
while (readAvailableDatagram(receivedPacket, senderSockAddr)) {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
switch (packetTypeForPacket(receivedPacket)) {
case PacketType::AvatarData: {
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
break;
}
case PacketType::AvatarIdentity: {
// check if we have a matching node in our list
SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket);
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();
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 PacketType::AvatarBillboard: {
// 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 PacketType::KillAvatar: {
nodeList->processKillNode(receivedPacket);
break;
}
default:
// hand this off to the NodeList
nodeList->processNodeData(senderSockAddr, receivedPacket);
break;
// 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;

View file

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

View file

@ -13,10 +13,9 @@
#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(QByteArray::fromRawData(packet.getPayload(), packet.getSizeUsed()));
}
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {

View file

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

View file

@ -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();
}

View file

@ -22,7 +22,7 @@
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
Q_OBJECT
public:
EntityServer(const QByteArray& packet);
EntityServer(NLPacket& packet);
~EntityServer();
// Subclasses must implement these methods
@ -49,6 +49,9 @@ public slots:
protected:
virtual Octree* createTree();
private slots:
void handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
private:
EntitySimulation* _entitySimulation;
QTimer* _pruneDeletedEntitiesTimer = nullptr;

View file

@ -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->getSizeUsed());
}
int numBytesPacketHeader = numBytesForPacketHeader(packet);
// Ask our tree subclass if it can handle the incoming packet...
PacketType::Value 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;
packet->readPrimitive(&sequence);
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
quint64 sentAt = (*((quint64*)(packetData + numBytesPacketHeader + sizeof(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,7 +117,7 @@ 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->getSizeWithHeader();
qDebug() << " sequence=" << sequence;
qDebug() << " sentAt=" << sentAt << " usecs";
qDebug() << " arrivedAt=" << arrivedAt << " usecs";
@ -125,42 +128,41 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin
}
if (debugProcessPacket) {
qDebug() << " numBytesPacketHeader=" << numBytesPacketHeader;
qDebug() << " numBytesPacketHeader=" << packet->localHeaderSize();
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->getSizeUsed();
if (!packet->bytesAvailable()) {
qDebug() << " ----- UNEXPECTED ---- got a packet without any edit details!!!! --------";
}
}
const unsigned char* editData = nullptr;
while (packet->bytesAvailable() > 0) {
unsigned char* editData = (unsigned char*)&packetData[atByte];
while (atByte < packet.size()) {
editData = reinterpret_cast<const unsigned char*>(packet->getPayload() + packet->pos());
int maxSize = packet.size() - atByte;
int maxSize = packet->bytesAvailable();
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->getSizeUsed(), 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 +179,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->getSizeUsed();
}
}
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->getSizeUsed(), 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;

View file

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

View file

@ -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,29 @@ void OctreeServer::parsePayload() {
}
}
void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
void OctreeServer::handleOctreeQueryPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
// 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);
// 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::Value 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 == PacketType::OctreeDataNack) {
// 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 == PacketType::JurisdictionRequest) {
_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);
}
}
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->getData());
}
}
// 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 +1027,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 +1048,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 +1122,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 +1140,7 @@ void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
quint64 start = usecTimestampNow();
qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
OctreeQueryNode* nodeData = dynamic_cast<OctreeQueryNode*>(node->getLinkedData());
if (nodeData) {
nodeData->forceNodeShutdown(); // tell our node data and sending threads that we'd like to shut down
} else {

View file

@ -32,7 +32,7 @@ const int DEFAULT_PACKETS_PER_INTERVAL = 2000; // some 120,000 packets per secon
class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
Q_OBJECT
public:
OctreeServer(const QByteArray& packet);
OctreeServer(NLPacket& packet);
~OctreeServer();
/// allows setting of run arguments
@ -125,8 +125,10 @@ public slots:
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;

View file

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

View file

@ -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,7 +279,16 @@ 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, "processConnectRequestPackets");
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,17 +576,18 @@ 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));
QDataStream packetStream(packet.data());
QUuid connectUUID;
packetStream >> connectUUID;
parseNodeDataFromByteArray(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
parseNodeData(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr);
// check if this connect request matches an assignment in the queue
bool isAssignment = _pendingAssignedNodes.contains(connectUUID);
@ -715,6 +726,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>();
@ -902,9 +931,9 @@ 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;
@ -933,15 +962,17 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
// always send the node their own UUID back
QDataStream domainListStream(&domainListPackets);
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + 2;
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);
extendedHeader.replace(0, NUM_BYTES_RFC4122_UUID, node->getUUID().toRfc4122());
QDataStream extendedHeaderStream(&extendedHeader, QIODevice::Append);
extendedHeader[NUM_BYTES_RFC4122_UUID] = (char) node->getCanAdjustLocks();
extendedHeader[NUM_BYTES_RFC4122_UUID + 1] = (char) node->getCanRez();
extendedHeaderStream << limitedNodeList->getSessionUUID().toRfc4122();
extendedHeaderStream << node->getUUID().toRfc4122();
extendedHeaderStream << (quint8) node->getCanAdjustLocks();
extendedHeaderStream << (quint8) node->getCanRez();
domainListPackets.setExtendedHeader(extendedHeader);
@ -1035,97 +1066,68 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
);
}
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;
while (limitedNodeList->getNodeSocket().hasPendingDatagrams()) {
receivedPacket.resize(limitedNodeList->getNodeSocket().pendingDatagramSize());
limitedNodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
if (packetTypeForPacket(receivedPacket) == PacketType::RequestAssignment
&& limitedNodeList->packetVersionAndHashMatch(receivedPacket)) {
if (!wasNoisyTimerStarted) {
noisyMessageTimer.start();
wasNoisyTimerStarted = true;
}
// construct the requested assignment from the packet data
Assignment requestAssignment(receivedPacket);
const qint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000;
// Suppress these for Assignment::AgentType to once per 5 seconds
static QElapsedTimer noisyMessageTimer;
static bool wasNoisyTimerStarted = false;
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();
}
if (!wasNoisyTimerStarted) {
noisyMessageTimer.start();
wasNoisyTimerStarted = true;
}
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
const qint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000;
if (assignmentToDeploy) {
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << packet->getSenderSockAddr();
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();
}
// give this assignment out, either the type matches or the requestor said they will take any
static std::unique_ptr<NLPacket> assignmentPacket;
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
if (!assignmentPacket) {
assignmentPacket = NLPacket::create(PacketType::CreateAssignment);
}
if (assignmentToDeploy) {
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr;
// setup a copy of this assignment that will have a unique UUID, for packaging purposes
Assignment uniqueAssignment(*assignmentToDeploy.data());
uniqueAssignment.setUUID(QUuid::createUuid());
// give this assignment out, either the type matches or the requestor said they will take any
static std::unique_ptr<NLPacket> assignmentPacket;
// reset the assignmentPacket
assignmentPacket->reset();
if (!assignmentPacket) {
assignmentPacket = NLPacket::create(PacketType::CreateAssignment);
}
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;
// reset the assignmentPacket
assignmentPacket->reset();
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
limitedNodeList->sendUnreliablePacket(*assignmentPacket, packet->getSenderSockAddr());
QDataStream assignmentStream(assignmentPacket.get());
assignmentStream << uniqueAssignment;
limitedNodeList->sendUnreliablePacket(*assignmentPacket, 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" << 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 std::unique_ptr<NLPacket> dtlsRequiredPacket;
if (!dtlsRequiredPacket) {
dtlsRequiredPacket = NLPacket::create(PacketType::DomainServerRequireDTLS, sizeof(unsigned short));
// pack the port that we accept DTLS traffic on
unsigned short dtlsPort = limitedNodeList->getDTLSSocket().localPort();
dtlsRequiredPacket->writePrimitive(dtlsPort);
}
limitedNodeList->sendUnreliablePacket(*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();
}
}
}
@ -1353,11 +1355,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;
@ -1382,86 +1383,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::Value requestType = packetTypeForPacket(receivedPacket);
switch (requestType) {
case PacketType::DomainConnectRequest:
handleConnectRequest(receivedPacket, senderSockAddr);
break;
case PacketType::DomainListRequest: {
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 PacketType::DomainServerPathQuery: {
// have our private method attempt to respond to this path query
respondToPathQuery(receivedPacket, senderSockAddr);
break;
}
case PacketType::NodeJsonStats: {
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (matchingNode) {
reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData())->parseJSONStatsPacket(receivedPacket);
}
break;
}
case PacketType::StunResponse:
nodeList->processSTUNResponse(receivedPacket);
break;
case PacketType::ICEPing: {
auto pingReplyPacket = nodeList->constructPingReplyPacket(receivedPacket);
nodeList->sendPacket(std::move(pingReplyPacket), senderSockAddr);
break;
}
case PacketType::ICEPingReply: {
processICEPingReply(receivedPacket, senderSockAddr);
break;
}
case PacketType::ICEServerPeerInformation:
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);
}
}
@ -2229,19 +2175,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 = 0;
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->bytesAvailable()) {
// 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("/")) {
@ -2289,7 +2233,7 @@ void DomainServer::respondToPathQuery(const QByteArray& receivedPacket, const Hi
// 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->sendPacket(std::move(pathResponsePacket), senderSockAddr);
nodeList->sendPacket(std::move(pathResponsePacket), packet->getSenderSockAddr());
}
}

View file

@ -24,6 +24,7 @@
#include <Assignment.h>
#include <HTTPSConnection.h>
#include <LimitedNodeList.h>
#include <PacketListener.h>
#include "DomainServerSettingsManager.h"
#include "DomainServerWebSessionData.h"
@ -34,7 +35,7 @@
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
class DomainServer : public QCoreApplication, public HTTPSRequestHandler, public PacketListener {
Q_OBJECT
public:
DomainServer(int argc, char* argv[]);
@ -55,11 +56,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();
@ -78,14 +87,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,
@ -94,11 +98,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);
@ -117,8 +118,6 @@ private:
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
void addStaticAssignmentsToQueue();
void respondToPathQuery(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
QUrl oauthRedirectURL();
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());

View file

@ -31,8 +31,8 @@ DomainServerNodeData::DomainServerNodeData() :
_paymentIntervalTimer.start();
}
void DomainServerNodeData::parseJSONStatsPacket(const QByteArray& statsPacket) {
QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(statsPacket.mid(numBytesForPacketHeader(statsPacket)));
void DomainServerNodeData::processJSONStatsPacket(NLPacket& statsPacket) {
QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(statsPacket.readAll());
_statsJSONObject = mergeJSONStatsFromNewObject(QJsonObject::fromVariantMap(packetVariantMap), _statsJSONObject);
}

View file

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

View file

@ -308,7 +308,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_window(new MainWindow(desktop())),
_toolWindow(NULL),
_friendsWindow(NULL),
_datagramProcessor(),
_undoStack(),
_undoStackScriptingInterface(&_undoStack),
_frameCount(0),
@ -381,19 +380,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("Datagram Processor Thread");
nodeThread->setObjectName("NodeList Thread");
nodeThread->start();
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
_datagramProcessor = new DatagramProcessor(nodeList.data());
// have the NodeList use deleteLater from DM customDeleter
nodeList->setCustomDeleter([](Dependency* dependency) {
static_cast<NodeList*>(dependency)->deleteLater();
});
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
@ -409,9 +401,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
ResourceCache* geometryCache = geometryCacheP.data();
connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets);
// connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams);
// put the audio processing on a separate thread
QThread* audioThread = new QThread();
audioThread->setObjectName("Audio Thread");
@ -422,14 +411,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
audioIO->setOrientationGetter(getOrientationForAudio);
audioIO->moveToThread(audioThread);
auto& audioScriptingInterface = AudioScriptingInterface::getInstance();
connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);
connect(audioIO.data(), &AudioClient::receivedFirstPacket,
&AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket);
connect(audioIO.data(), &AudioClient::disconnected,
&AudioScriptingInterface::getInstance(), &AudioScriptingInterface::disconnected);
connect(audioIO.data(), &AudioClient::mutedByMixer, &audioScriptingInterface, &AudioScriptingInterface::mutedByMixer);
connect(audioIO.data(), &AudioClient::receivedFirstPacket, &audioScriptingInterface, &AudioScriptingInterface::receivedFirstPacket);
connect(audioIO.data(), &AudioClient::disconnected, &audioScriptingInterface, &AudioScriptingInterface::disconnected);
connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) {
auto audioClient = DependencyManager::get<AudioClient>();
float distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(),
position);
bool shouldMute = !audioClient->isMuted() && (distance < radius);
if (shouldMute) {
audioClient->toggleMute();
AudioScriptingInterface::getInstance().environmentMuted();
}
});
audioThread->start();
@ -462,7 +464,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(nodeList.data(), &NodeList::uuidChanged, _myAvatar, &MyAvatar::setSessionUUID);
connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);
connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
connect(&nodeList->getPacketReceiver(), &PacketReceiver::packetVersionMismatch,
this, &Application::notifyPacketVersionMismatch);
// connect to appropriate slots on AccountManager
AccountManager& accountManager = AccountManager::getInstance();
@ -646,6 +649,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
applicationUpdater->checkForUpdate();
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
}
void Application::aboutToQuit() {
@ -658,8 +664,10 @@ void Application::aboutToQuit() {
void Application::cleanupBeforeQuit() {
_entities.clear(); // this will allow entity scripts to properly shutdown
_datagramProcessor->shutdown(); // tell the datagram processor we're shutting down, so it can short circuit
// tell the packet receiver we're shutting down, so it can drop packets
DependencyManager::get<NodeList>()->getPacketReceiver().setShouldDropPackets(true);
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
@ -734,6 +742,8 @@ Application::~Application() {
DependencyManager::destroy<SoundCache>();
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
@ -1787,7 +1797,6 @@ void Application::checkFPS() {
_fps = (float)_frameCount / diffTime;
_frameCount = 0;
_datagramProcessor->resetCounters();
_timerStart.start();
}
@ -3809,11 +3818,23 @@ void Application::domainChanged(const QString& domainHostname) {
_domainConnectionRefusals.clear();
}
void Application::domainConnectionDenied(const QString& reason) {
void Application::handleDomainConnectionDeniedPacket(QSharedPointer<NLPacket> packet) {
QDataStream packetStream(packet.data());
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.";
if (!_domainConnectionRefusals.contains(reason)) {
_domainConnectionRefusals.append(reason);
emit domainConnectionRefused(reason);
}
AccountManager::getInstance().checkAndSignalForAccessToken();
}
void Application::connectedToDomain(const QString& hostname) {
@ -3889,12 +3910,12 @@ void Application::nodeKilled(SharedNodePointer node) {
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
}
}
void Application::trackIncomingOctreePacket(const QByteArray& packet, const SharedNodePointer& sendingNode, bool wasStatsPacket) {
void Application::trackIncomingOctreePacket(NLPacket& packet, SharedNodePointer sendingNode, bool wasStatsPacket) {
// Attempt to identify the sender from its address.
if (sendingNode) {
QUuid nodeUUID = sendingNode->getUUID();
const QUuid& nodeUUID = sendingNode->getUUID();
// now that we know the node ID, let's add these stats to the stats for that node...
_octreeSceneStatsLock.lockForWrite();
@ -3906,70 +3927,71 @@ void Application::trackIncomingOctreePacket(const QByteArray& packet, const Shar
}
}
int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sendingNode) {
int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingNode) {
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
// parse the incoming stats datas stick it in a temporary object for now, while we
// determine which server it belongs to
OctreeSceneStats temp;
int statsMessageLength = temp.unpackFromMessage(reinterpret_cast<const unsigned char*>(packet.data()), packet.size());
int statsMessageLength = 0;
// quick fix for crash... why would voxelServer be NULL?
if (sendingNode) {
QUuid nodeUUID = sendingNode->getUUID();
const QUuid& nodeUUID = sendingNode->getUUID();
OctreeSceneStats* octreeStats;
// now that we know the node ID, let's add these stats to the stats for that node...
_octreeSceneStatsLock.lockForWrite();
if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) {
_octreeServerSceneStats[nodeUUID].unpackFromMessage(reinterpret_cast<const unsigned char*>(packet.data()),
packet.size());
} else {
_octreeServerSceneStats[nodeUUID] = temp;
// now that we know the node ID, let's add these stats to the stats for that node...
_octreeSceneStatsLock.lockForWrite();
auto it = _octreeServerSceneStats.find(nodeUUID);
if (it != _octreeServerSceneStats.end()) {
octreeStats = &it->second;
statsMessageLength = octreeStats->unpackFromPacket(packet);
} else {
OctreeSceneStats temp;
statsMessageLength = temp.unpackFromPacket(packet);
octreeStats = &temp;
}
_octreeSceneStatsLock.unlock();
VoxelPositionSize rootDetails;
voxelDetailsForCode(octreeStats->getJurisdictionRoot(), rootDetails);
// see if this is the first we've heard of this node...
NodeToJurisdictionMap* jurisdiction = NULL;
QString serverType;
if (sendingNode->getType() == NodeType::EntityServer) {
jurisdiction = &_entityServerJurisdictions;
serverType = "Entity";
}
jurisdiction->lockForRead();
if (jurisdiction->find(nodeUUID) == jurisdiction->end()) {
jurisdiction->unlock();
qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]",
qPrintable(serverType),
(double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs"
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) {
OctreeFade fade(OctreeFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_octreeFadesLock.lockForWrite();
_octreeFades.push_back(fade);
_octreeFadesLock.unlock();
}
_octreeSceneStatsLock.unlock();
VoxelPositionSize rootDetails;
voxelDetailsForCode(temp.getJurisdictionRoot(), rootDetails);
// see if this is the first we've heard of this node...
NodeToJurisdictionMap* jurisdiction = NULL;
QString serverType;
if (sendingNode->getType() == NodeType::EntityServer) {
jurisdiction = &_entityServerJurisdictions;
serverType = "Entity";
}
jurisdiction->lockForRead();
if (jurisdiction->find(nodeUUID) == jurisdiction->end()) {
jurisdiction->unlock();
qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]",
qPrintable(serverType),
(double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs"
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) {
OctreeFade fade(OctreeFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99f;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_octreeFadesLock.lockForWrite();
_octreeFades.push_back(fade);
_octreeFadesLock.unlock();
}
} else {
jurisdiction->unlock();
}
// store jurisdiction details for later use
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it
// but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the
// details from the OctreeSceneStats to construct the JurisdictionMap
JurisdictionMap jurisdictionMap;
jurisdictionMap.copyContents(temp.getJurisdictionRoot(), temp.getJurisdictionEndNodes());
jurisdiction->lockForWrite();
(*jurisdiction)[nodeUUID] = jurisdictionMap;
} else {
jurisdiction->unlock();
}
// store jurisdiction details for later use
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it
// but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the
// details from the OctreeSceneStats to construct the JurisdictionMap
JurisdictionMap jurisdictionMap;
jurisdictionMap.copyContents(octreeStats->getJurisdictionRoot(), octreeStats->getJurisdictionEndNodes());
jurisdiction->lockForWrite();
(*jurisdiction)[nodeUUID] = jurisdictionMap;
jurisdiction->unlock();
return statsMessageLength;
}
@ -4809,6 +4831,7 @@ mat4 Application::getEyeProjection(int eye) const {
if (isHMDMode()) {
return OculusManager::getEyeProjection(eye);
}
return _viewFrustum.getProjection();
}

View file

@ -32,6 +32,7 @@
#include <OctreeQuery.h>
#include <OffscreenUi.h>
#include <udt/PacketHeaders.h>
#include <PacketListener.h>
#include <PhysicalEntitySimulation.h>
#include <PhysicsEngine.h>
#include <ScriptEngine.h>
@ -43,7 +44,6 @@
#include "AudioClient.h"
#include "Bookmarks.h"
#include "Camera.h"
#include "DatagramProcessor.h"
#include "Environment.h"
#include "FileLogger.h"
#include "GLCanvas.h"
@ -135,7 +135,10 @@ class Application;
typedef bool (Application::* AcceptURLMethod)(const QString &);
class Application : public QApplication, public AbstractViewStateInterface, AbstractScriptingServicesInterface {
class Application : public QApplication,
public AbstractViewStateInterface,
AbstractScriptingServicesInterface,
public PacketListener {
Q_OBJECT
friend class OctreePacketProcessor;
@ -214,6 +217,7 @@ 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; }
@ -444,7 +448,7 @@ public slots:
void notifyPacketVersionMismatch();
void domainConnectionDenied(const QString& reason);
void handleDomainConnectionDeniedPacket(QSharedPointer<NLPacket> packet);
void cameraMenuChanged();
@ -528,8 +532,6 @@ private:
ToolWindow* _toolWindow;
WebWindowClass* _friendsWindow;
DatagramProcessor* _datagramProcessor;
QUndoStack _undoStack;
UndoStackScriptingInterface _undoStackScriptingInterface;
@ -615,8 +617,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;

View file

@ -1,169 +0,0 @@
//
// DatagramProcessor.cpp
// interface/src
//
// Created by Stephen Birarda on 1/23/2014.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QWeakPointer>
#include <AccountManager.h>
#include <PerfStat.h>
#include "Application.h"
#include "avatar/AvatarManager.h"
#include "AudioClient.h"
#include "Menu.h"
#include "InterfaceLogging.h"
#include "DatagramProcessor.h"
DatagramProcessor::DatagramProcessor(QObject* parent) :
QObject(parent)
{
}
void DatagramProcessor::processDatagrams() {
if (_isShuttingDown) {
return; // bail early... we're shutting down.
}
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"DatagramProcessor::processDatagrams()");
HifiSockAddr senderSockAddr;
static QByteArray incomingPacket;
Application* application = Application::getInstance();
auto nodeList = DependencyManager::get<NodeList>();
while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) {
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
_inPacketCount++;
_inByteCount += incomingPacket.size();
if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
PacketType::Value incomingType = packetTypeForPacket(incomingPacket);
// only process this packet if we have a match on the packet version
switch (incomingType) {
case PacketType::AudioEnvironment:
case PacketType::AudioStreamStats:
case PacketType::MixedAudio:
case PacketType::SilentAudioFrame: {
if (incomingType == PacketType::AudioStreamStats) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioStreamStatsPacket",
Qt::QueuedConnection,
Q_ARG(QByteArray, incomingPacket));
} else if (incomingType == PacketType::AudioEnvironment) {
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 PacketType::EntityData:
case PacketType::EntityErase:
case PacketType::OctreeStats:
case PacketType::EnvironmentData: {
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 PacketType::BulkAvatarData:
case PacketType::KillAvatar:
case PacketType::AvatarIdentity:
case PacketType::AvatarBillboard: {
// 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 PacketType::DomainConnectionDenied: {
int headerSize = 0;
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 PacketType::NoisyMute:
case PacketType::MuteEnvironment: {
bool mute = !DependencyManager::get<AudioClient>()->isMuted();
if (incomingType == PacketType::MuteEnvironment) {
glm::vec3 position;
float radius;
int headerSize = 0;
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 == PacketType::MuteEnvironment) {
AudioScriptingInterface::getInstance().environmentMuted();
} else {
AudioScriptingInterface::getInstance().mutedByMixer();
}
}
break;
}
case PacketType::EntityEditNack:
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) {
application->_entityEditSender.processNackPacket(incomingPacket);
}
break;
default:
nodeList->processNodeData(senderSockAddr, incomingPacket);
break;
}
}
}
}

View file

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

View file

@ -500,7 +500,9 @@ Menu::Menu() {
#endif
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false,
qApp->getEntityEditPacketSender(),
SLOT(toggleNackPackets()));
addCheckableActionToQMenuAndActionHash(networkMenu,
MenuOption::DisableActivityLogger,
0,

View file

@ -169,7 +169,7 @@ namespace MenuOption {
const QString DeleteBookmark = "Delete Bookmark...";
const QString DisableActivityLogger = "Disable Activity Logger";
const QString DisableLightEntities = "Disable Light Entities";
const QString DisableNackPackets = "Disable NACK Packets";
const QString DisableNackPackets = "Disable Entity NACK Packets";
const QString DiskCacheEditor = "Disk Cache Editor";
const QString DisplayHands = "Show Hand Info";
const QString DisplayHandTargets = "Show Hand Targets";

View file

@ -1002,7 +1002,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();
@ -1011,7 +1011,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;
@ -1137,7 +1137,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
}
}
// virtual
// virtual
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight());

View file

@ -85,10 +85,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 +146,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 +163,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 +179,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 +203,9 @@ protected:
// These position histories and derivatives are in the world-frame.
// The derivatives are the MEASURED results of all external and internal forces
// and are therefore READ-ONLY --> motion control of the Avatar is NOT obtained
// and are therefore READ-ONLY --> motion control of the Avatar is NOT obtained
// by setting these values.
// Floating point error prevents us from accurately measuring velocity using a naive approach
// Floating point error prevents us from accurately measuring velocity using a naive approach
// (e.g. vel = (pos - lastPos)/dt) so instead we use _positionDeltaAccumulator.
glm::vec3 _positionDeltaAccumulator;
glm::vec3 _lastVelocity;

View file

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

View file

@ -870,12 +870,11 @@ 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() {

View file

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

View file

@ -1,4 +1,4 @@
//Merge branch 'master' of ssh://github.com/highfidelity/hifi into isentropic/
//
// OctreePacketProcessor.cpp
// interface/src/octree
//
@ -16,91 +16,104 @@
#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, PacketType::EnvironmentData
};
packetReceiver.registerListenerForTypes(types, this, "handleOctreePacket");
}
OctreePacketProcessor::~OctreePacketProcessor() {
DependencyManager::get<NodeList>()->getPacketReceiver().unregisterListener(this);
}
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 voxelPacketType = packetTypeForPacket(mutablePacket);
PacketType::Value octreePacketType = packet->getType();
// 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 == PacketType::OctreeStats) {
int statsMessageLength = app->parseOctreeStats(mutablePacket, sendingNode);
wasStatsPacket = true;
if (messageLength > statsMessageLength) {
mutablePacket = mutablePacket.mid(statsMessageLength);
if (octreePacketType == PacketType::OctreeStats) {
int statsMessageLength = app->processOctreeStats(*packet, sendingNode);
// 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
}
wasStatsPacket = true;
int piggybackBytes = packet->getSizeUsed() - 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);
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) {
if (packetType != versionForPacketType(packet->getType())) {
static QMultiMap<QUuid, PacketType::Value> 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.";
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(mutablePacket, sendingNode, wasStatsPacket);
app->trackIncomingOctreePacket(*packet, sendingNode, wasStatsPacket);
if (sendingNode) {
switch(packetType) {
case PacketType::EntityErase: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
app->_entities.processEraseMessage(*packet, sendingNode);
}
} break;
switch(voxelPacketType) {
case PacketType::EntityErase: {
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 PacketType::EntityData: {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
app->_entities.processDatagram(mutablePacket, sendingNode);
}
} break;
case PacketType::EnvironmentData: {
app->_environment.processPacket(*packet);
} break;
case PacketType::EnvironmentData: {
app->_environment.parseData(*sendingNode->getActiveSocket(), mutablePacket);
} break;
default: {
// nothing to do
} break;
}
default: {
// nothing to do
} break;
}
}

View file

@ -13,16 +13,23 @@
#define hifi_OctreePacketProcessor_h
#include <ReceivedPacketProcessor.h>
#include <PacketListener.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 {
class OctreePacketProcessor : public ReceivedPacketProcessor, public PacketListener {
Q_OBJECT
public:
OctreePacketProcessor();
~OctreePacketProcessor();
signals:
void packetVersionMismatch();
protected:
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
private slots:
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
};
#endif // hifi_OctreePacketProcessor_h

View file

@ -33,7 +33,7 @@
#include <QtMultimedia/QAudioInput>
#include <QtMultimedia/QAudioOutput>
#ifdef __GNUC__
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdouble-promotion"
#endif
@ -43,7 +43,7 @@ extern "C" {
#include <gverb/gverbdsp.h>
}
#ifdef __GNUC__
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
@ -140,6 +140,14 @@ AudioClient::AudioClient() :
// create GVerb filter
_gverb = createGverbFilter();
configureGverbFilter(_gverb);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AudioStreamStats, &_stats, "handleAudioStreamStatsPacket");
packetReceiver.registerListener(PacketType::AudioEnvironment, this, "handleAudioEnvironmentDataPacket");
packetReceiver.registerListener(PacketType::SilentAudioFrame, this, "handleAudioDataPacket");
packetReceiver.registerListener(PacketType::MixedAudio, this, "handleAudioDataPacket");
packetReceiver.registerListener(PacketType::NoisyMute, this, "handleNoisyMutePacket");
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
}
AudioClient::~AudioClient() {
@ -527,6 +535,60 @@ void AudioClient::stop() {
}
}
void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<NLPacket> packet) {
char bitset;
packet->readPrimitive(&bitset);
bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);
if (hasReverb) {
float reverbTime, wetLevel;
packet->readPrimitive(&reverbTime);
packet->readPrimitive(&wetLevel);
_receivedAudioStream.setReverb(reverbTime, wetLevel);
} else {
_receivedAudioStream.clearReverb();
}
}
void AudioClient::handleAudioDataPacket(QSharedPointer<NLPacket> packet) {
auto nodeList = DependencyManager::get<NodeList>();
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
if (_audioOutput) {
if (!_hasReceivedFirstPacket) {
_hasReceivedFirstPacket = true;
// have the audio scripting interface emit a signal to say we just connected to mixer
emit receivedFirstPacket();
}
// Audio output must exist and be correctly set up if we're going to process received audio
_receivedAudioStream.parseData(*packet);
}
}
void AudioClient::handleNoisyMutePacket(QSharedPointer<NLPacket> packet) {
if (!_muted) {
toggleMute();
// have the audio scripting interface emit a signal to say we were muted by the mixer
emit mutedByMixer();
}
}
void AudioClient::handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet) {
glm::vec3 position;
float radius;
packet->readPrimitive(&position);
packet->readPrimitive(&radius);
emit muteEnvironmentRequested(position, radius);
}
QString AudioClient::getDefaultDeviceName(QAudio::Mode mode) {
QAudioDeviceInfo deviceInfo = defaultAudioDeviceForMode(mode);
return deviceInfo.deviceName();
@ -928,44 +990,6 @@ void AudioClient::sendMuteEnvironmentPacket() {
}
}
void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) {
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
if (_audioOutput) {
if (!_hasReceivedFirstPacket) {
_hasReceivedFirstPacket = true;
// have the audio scripting interface emit a signal to say we just connected to mixer
emit receivedFirstPacket();
}
// Audio output must exist and be correctly set up if we're going to process received audio
_receivedAudioStream.parseData(audioByteArray);
}
}
void AudioClient::parseAudioEnvironmentData(const QByteArray &packet) {
int numBytesPacketHeader = numBytesForPacketHeader(packet);
const char* dataAt = packet.constData() + numBytesPacketHeader;
char bitset;
memcpy(&bitset, dataAt, sizeof(char));
dataAt += sizeof(char);
bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);;
if (hasReverb) {
float reverbTime, wetLevel;
memcpy(&reverbTime, dataAt, sizeof(float));
dataAt += sizeof(float);
memcpy(&wetLevel, dataAt, sizeof(float));
dataAt += sizeof(float);
_receivedAudioStream.setReverb(reverbTime, wetLevel);
} else {
_receivedAudioStream.clearReverb();
}
}
void AudioClient::toggleMute() {
_muted = !_muted;
emit muteToggled();

View file

@ -13,6 +13,7 @@
#define hifi_AudioClient_h
#include <fstream>
#include <memory>
#include <vector>
#include <QtCore/QByteArray>
@ -32,10 +33,13 @@
#include <AudioSourceTone.h>
#include <AudioSourceNoise.h>
#include <AudioStreamStats.h>
#include <DependencyManager.h>
#include <DependencyManager.h>
#include <HifiSockAddr.h>
#include <NLPacket.h>
#include <MixedProcessedAudioStream.h>
#include <RingBufferHistory.h>
#include <PacketListener.h>
#include <SettingHandle.h>
#include <Sound.h>
#include <StDev.h>
@ -77,7 +81,7 @@ typedef glm::quat (*AudioOrientationGetter)();
class NLPacket;
class AudioClient : public AbstractAudioInterface, public Dependency {
class AudioClient : public AbstractAudioInterface, public Dependency, public PacketListener {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
@ -135,10 +139,13 @@ public:
public slots:
void start();
void stop();
void addReceivedAudioToStream(const QByteArray& audioByteArray);
void parseAudioEnvironmentData(const QByteArray& packet);
void handleAudioEnvironmentDataPacket(QSharedPointer<NLPacket> packet);
void handleAudioDataPacket(QSharedPointer<NLPacket> packet);
void handleNoisyMutePacket(QSharedPointer<NLPacket> packet);
void handleMuteEnvironmentPacket(QSharedPointer<NLPacket> packet);
void sendDownstreamAudioStatsPacket() { _stats.sendDownstreamAudioStatsPacket(); }
void parseAudioStreamStatsPacket(const QByteArray& packet) { _stats.parseAudioStreamStatsPacket(packet); }
void handleAudioInput();
void reset();
void audioMixerKilled();
@ -181,6 +188,7 @@ public slots:
signals:
bool muteToggled();
void mutedByMixer();
void inputReceived(const QByteArray& inputSamples);
void outputBytesToNetwork(int numBytes);
void inputBytesFromNetwork(int numBytes);
@ -192,6 +200,8 @@ signals:
void audioFinished();
void muteEnvironmentRequested(glm::vec3 position, float radius);
protected:
AudioClient();
~AudioClient();

View file

@ -63,27 +63,24 @@ void AudioIOStats::sentPacket() {
_lastSentAudioPacket = now;
}
}
void AudioIOStats::parseAudioStreamStatsPacket(const QByteArray& packet) {
int numBytesPacketHeader = numBytesForPacketHeader(packet);
const char* dataAt = packet.constData() + numBytesPacketHeader;
void AudioIOStats::processStreamStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
// parse the appendFlag, clear injected audio stream stats if 0
quint8 appendFlag = *(reinterpret_cast<const quint16*>(dataAt));
dataAt += sizeof(quint8);
quint8 appendFlag;
packet->readPrimitive(&appendFlag);
if (!appendFlag) {
_mixerInjectedStreamStatsMap.clear();
}
// parse the number of stream stats structs to follow
quint16 numStreamStats = *(reinterpret_cast<const quint16*>(dataAt));
dataAt += sizeof(quint16);
quint16 numStreamStats;
packet->readPrimitive(&numStreamStats);
// parse the stream stats
AudioStreamStats streamStats;
for (quint16 i = 0; i < numStreamStats; i++) {
memcpy(&streamStats, dataAt, sizeof(AudioStreamStats));
dataAt += sizeof(AudioStreamStats);
packet->readPrimitive(&streamStats);
if (streamStats._streamType == PositionalAudioStream::Microphone) {
_mixerAvatarStreamStats = streamStats;

View file

@ -17,10 +17,13 @@
#include <QObject>
#include <AudioStreamStats.h>
#include <Node.h>
#include <NLPacket.h>
#include <PacketListener.h>
class MixedProcessedAudioStream;
class AudioIOStats : public QObject {
class AudioIOStats : public QObject, public PacketListener {
Q_OBJECT
public:
AudioIOStats(MixedProcessedAudioStream* receivedAudioStream);
@ -41,7 +44,7 @@ public:
const MovingMinMaxAvg<quint64>& getPacketSentTimeGaps() const { return _packetSentTimeGaps; }
void sendDownstreamAudioStatsPacket();
void parseAudioStreamStatsPacket(const QByteArray& packet);
void processStreamStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
private:
MixedProcessedAudioStream* _receivedAudioStream;
@ -57,4 +60,4 @@ private:
MovingMinMaxAvg<quint64> _packetSentTimeGaps;
};
#endif // hifi_AudioIOStats_h
#endif // hifi_AudioIOStats_h

View file

@ -11,6 +11,9 @@
#include <glm/glm.hpp>
#include <NLPacket.h>
#include <Node.h>
#include "InboundAudioStream.h"
#include "udt/PacketHeaders.h"
@ -96,28 +99,23 @@ void InboundAudioStream::perSecondCallbackForUpdatingStats() {
_timeGapStatsForStatsPacket.currentIntervalComplete();
}
int InboundAudioStream::parseData(const QByteArray& packet) {
PacketType::Value packetType = packetTypeForPacket(packet);
QUuid senderUUID = uuidFromPacketHeader(packet);
// parse header
int numBytesHeader = numBytesForPacketHeader(packet);
const char* dataAt = packet.constData() + numBytesHeader;
int readBytes = numBytesHeader;
int InboundAudioStream::parseData(NLPacket& packet) {
// parse sequence number and track it
quint16 sequence = *(reinterpret_cast<const quint16*>(dataAt));
dataAt += sizeof(quint16);
readBytes += sizeof(quint16);
SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence, senderUUID);
quint16 sequence;
packet.readPrimitive(&sequence);
SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence,
packet.getSourceID());
packetReceivedUpdateTimingStats();
int networkSamples;
// parse the info after the seq number and before the audio data (the stream properties)
readBytes += parseStreamProperties(packetType, packet.mid(readBytes), networkSamples);
int propertyBytes = parseStreamProperties(packet.getType(),
QByteArray::fromRawData(packet.getPayload(), packet.pos()),
networkSamples);
packet.seek(packet.pos() + propertyBytes);
// handle this packet based on its arrival status.
switch (arrivalInfo._status) {
@ -132,10 +130,12 @@ int InboundAudioStream::parseData(const QByteArray& packet) {
}
case SequenceNumberStats::OnTime: {
// Packet is on time; parse its data to the ringbuffer
if (packetType == PacketType::SilentAudioFrame) {
if (packet.getType() == PacketType::SilentAudioFrame) {
writeDroppableSilentSamples(networkSamples);
} else {
readBytes += parseAudioData(packetType, packet.mid(readBytes), networkSamples);
int audioBytes = parseAudioData(packet.getType(), QByteArray::fromRawData(packet.getPayload(), packet.pos()),
networkSamples);
packet.seek(packet.pos() + audioBytes);
}
break;
}
@ -165,7 +165,7 @@ int InboundAudioStream::parseData(const QByteArray& packet) {
framesAvailableChanged();
return readBytes;
return packet.pos();
}
int InboundAudioStream::parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
@ -314,7 +314,7 @@ void InboundAudioStream::setToStarved() {
starvesInWindow++;
} while (starvesIterator != end);
// this starve put us over the starve threshold. update _desiredJitterBufferFrames to
// this starve put us over the starve threshold. update _desiredJitterBufferFrames to
// value determined by window A.
if (starvesInWindow >= _starveThreshold) {
int calculatedJitterBufferFrames;
@ -398,7 +398,7 @@ void InboundAudioStream::packetReceivedUpdateTimingStats() {
_timeGapStatsForDesiredReduction.update(gap);
if (_timeGapStatsForDesiredCalcOnTooManyStarves.getNewStatsAvailableFlag()) {
_calculatedJitterBufferFramesUsingMaxGap = ceilf((float)_timeGapStatsForDesiredCalcOnTooManyStarves.getWindowMax()
_calculatedJitterBufferFramesUsingMaxGap = ceilf((float)_timeGapStatsForDesiredCalcOnTooManyStarves.getWindowMax()
/ (float) AudioConstants::NETWORK_FRAME_USECS);
_timeGapStatsForDesiredCalcOnTooManyStarves.clearNewStatsAvailableFlag();
}

View file

@ -80,7 +80,7 @@ public:
{}
// max number of frames over desired in the ringbuffer.
int _maxFramesOverDesired;
int _maxFramesOverDesired;
// if false, _desiredJitterBufferFrames will always be _staticDesiredJitterBufferFrames. Otherwise,
// either fred or philip's method will be used to calculate _desiredJitterBufferFrames based on packet timegaps.
@ -107,7 +107,7 @@ public:
virtual void resetStats();
void clearBuffer();
virtual int parseData(const QByteArray& packet);
virtual int parseData(NLPacket& packet);
int popFrames(int maxFrames, bool allOrNothing, bool starveIfNoFramesPopped = true);
int popSamples(int maxSamples, bool allOrNothing, bool starveIfNoSamplesPopped = true);
@ -131,7 +131,7 @@ public:
virtual AudioStreamStats getAudioStreamStats() const;
/// returns the desired number of jitter buffer frames under the dyanmic jitter buffers scheme
int getCalculatedJitterBufferFrames() const { return _useStDevForJitterCalc ?
int getCalculatedJitterBufferFrames() const { return _useStDevForJitterCalc ?
_calculatedJitterBufferFramesUsingStDev : _calculatedJitterBufferFramesUsingMaxGap; };
/// returns the desired number of jitter buffer frames using Philip's method
@ -217,7 +217,7 @@ protected:
bool _dynamicJitterBuffers; // if false, _desiredJitterBufferFrames is locked at 1 (old behavior)
int _staticDesiredJitterBufferFrames;
// if jitter buffer is dynamic, this determines what method of calculating _desiredJitterBufferFrames
// if jitter buffer is dynamic, this determines what method of calculating _desiredJitterBufferFrames
// if true, Philip's timegap std dev calculation is used. Otherwise, Freddy's max timegap calculation is used
bool _useStDevForJitterCalc;

View file

@ -265,7 +265,7 @@ bool AvatarData::shouldLogError(const quint64& now) {
}
// read data in packet starting at byte offset and return number of bytes parsed
int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
// lazily allocate memory for HeadData in case we're not an Avatar instance
if (!_headData) {
@ -277,7 +277,7 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
_handData = new HandData(this);
}
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(packet.data()) + offset;
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(buffer.data());
const unsigned char* sourceBuffer = startPosition;
quint64 now = usecTimestampNow();
@ -299,7 +299,7 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
// = 45 bytes
int minPossibleSize = 45;
int maxAvailableSize = packet.size() - offset;
int maxAvailableSize = buffer.size();
if (minPossibleSize > maxAvailableSize) {
if (shouldLogError(now)) {
qCDebug(avatars) << "Malformed AvatarData packet at the start; "
@ -867,9 +867,8 @@ void AvatarData::clearJointsData() {
}
}
bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
bool AvatarData::hasIdentityChangedAfterParsing(NLPacket& packet) {
QDataStream packetStream(&packet);
QUuid avatarUUID;
QUrl faceModelURL, skeletonModelURL;
@ -911,8 +910,8 @@ QByteArray AvatarData::identityByteArray() {
return identityData;
}
bool AvatarData::hasBillboardChangedAfterParsing(const QByteArray& packet) {
QByteArray newBillboard = packet.mid(numBytesForPacketHeader(packet));
bool AvatarData::hasBillboardChangedAfterParsing(NLPacket& packet) {
QByteArray newBillboard = packet.readAll();
if (newBillboard == _billboard) {
return false;
}

View file

@ -47,6 +47,7 @@ typedef unsigned long long quint64;
#include <QReadWriteLock>
#include <CollisionInfo.h>
#include <NLPacket.h>
#include <Node.h>
#include <RegisteredMetaTypes.h>
#include <SimpleMovingAverage.h>
@ -66,12 +67,12 @@ typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0;
const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1;
const quint32 AVATAR_MOTION_DEFAULTS =
const quint32 AVATAR_MOTION_DEFAULTS =
AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED |
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
// these bits will be expanded as features are exposed
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
@ -181,7 +182,7 @@ public:
/// \param packet byte array of data
/// \param offset number of bytes into packet where data starts
/// \return number of bytes parsed
virtual int parseDataAtOffset(const QByteArray& packet, int offset);
virtual int parseDataFromBuffer(const QByteArray& buffer);
// Body Rotation (degrees)
float getBodyYaw() const { return _bodyYaw; }
@ -241,7 +242,7 @@ public:
Q_INVOKABLE virtual void clearJointsData();
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return _jointIndices.value(name) - 1; }
Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return _jointIndices.value(name) - 1; }
Q_INVOKABLE virtual QStringList getJointNames() const { return _jointNames; }
@ -260,10 +261,10 @@ public:
return false;
}
bool hasIdentityChangedAfterParsing(const QByteArray& packet);
bool hasIdentityChangedAfterParsing(NLPacket& packet);
QByteArray identityByteArray();
bool hasBillboardChangedAfterParsing(const QByteArray& packet);
bool hasBillboardChangedAfterParsing(NLPacket& packet);
const QUrl& getFaceModelURL() const { return _faceModelURL; }
QString getFaceModelURLString() const { return _faceModelURL.toString(); }

View file

@ -2,7 +2,7 @@
// AvatarHashMap.cpp
// libraries/avatars/src
//
// Created by AndrewMeadows on 1/28/2014.
// Created by Andrew Meadows on 1/28/2014.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -18,25 +18,12 @@
AvatarHashMap::AvatarHashMap() {
connect(DependencyManager::get<NodeList>().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
}
void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
switch (packetTypeForPacket(datagram)) {
case PacketType::BulkAvatarData:
processAvatarDataPacket(datagram, mixerWeakPointer);
break;
case PacketType::AvatarIdentity:
processAvatarIdentityPacket(datagram, mixerWeakPointer);
break;
case PacketType::AvatarBillboard:
processAvatarBillboardPacket(datagram, mixerWeakPointer);
break;
case PacketType::KillAvatar:
processKillAvatar(datagram);
break;
default:
break;
}
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");
}
bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) {
@ -65,86 +52,84 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe
return avatar;
}
void AvatarHashMap::processAvatarDataPacket(const QByteArray &datagram, const QWeakPointer<Node> &mixerWeakPointer) {
int bytesRead = numBytesForPacketHeader(datagram);
void AvatarHashMap::processAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
// enumerate over all of the avatars in this packet
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
while (bytesRead < datagram.size() && mixerWeakPointer.data()) {
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
bytesRead += NUM_BYTES_RFC4122_UUID;
while (packet->bytesAvailable()) {
QUuid sessionUUID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID));
if (sessionUUID != _lastOwnerSessionUUID) {
AvatarSharedPointer avatar = _avatarHash.value(sessionUUID);
if (!avatar) {
avatar = addAvatar(sessionUUID, mixerWeakPointer);
avatar = addAvatar(sessionUUID, sendingNode);
}
// have the matching (or new) avatar parse the data from the packet
bytesRead += avatar->parseDataAtOffset(datagram, bytesRead);
int bytesRead = avatar->parseDataFromBuffer(QByteArray::fromRawData(packet->getPayload(), packet->pos()));
packet->seek(packet->pos() + bytesRead);
} else {
// create a dummy AvatarData class to throw this data on the ground
AvatarData dummyData;
bytesRead += dummyData.parseDataAtOffset(datagram, bytesRead);
int bytesRead = dummyData.parseDataFromBuffer(QByteArray::fromRawData(packet->getPayload(), packet->pos()));
packet->seek(packet->pos() + bytesRead);
}
}
}
void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const QWeakPointer<Node>& mixerWeakPointer) {
void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
// setup a data stream to parse the packet
QDataStream identityStream(packet);
identityStream.skipRawData(numBytesForPacketHeader(packet));
QDataStream identityStream(packet.data());
QUuid sessionUUID;
while (!identityStream.atEnd()) {
QUrl faceMeshURL, skeletonURL;
QVector<AttachmentData> attachmentData;
QString displayName;
identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName;
// mesh URL for a UUID, find avatar in our list
AvatarSharedPointer avatar = _avatarHash.value(sessionUUID);
if (!avatar) {
avatar = addAvatar(sessionUUID, mixerWeakPointer);
avatar = addAvatar(sessionUUID, sendingNode);
}
if (avatar->getFaceModelURL() != faceMeshURL) {
avatar->setFaceModelURL(faceMeshURL);
}
if (avatar->getSkeletonModelURL() != skeletonURL) {
avatar->setSkeletonModelURL(skeletonURL);
}
if (avatar->getAttachmentData() != attachmentData) {
avatar->setAttachmentData(attachmentData);
}
if (avatar->getDisplayName() != displayName) {
avatar->setDisplayName(displayName);
}
}
}
void AvatarHashMap::processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer) {
int headerSize = numBytesForPacketHeader(packet);
QUuid sessionUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID));
void AvatarHashMap::processAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
QUuid sessionUUID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID));
AvatarSharedPointer avatar = _avatarHash.value(sessionUUID);
if (!avatar) {
avatar = addAvatar(sessionUUID, mixerWeakPointer);
avatar = addAvatar(sessionUUID, sendingNode);
}
QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID);
QByteArray billboard = packet->read(packet->bytesAvailable());
if (avatar->getBillboard() != billboard) {
avatar->setBillboard(billboard);
}
}
void AvatarHashMap::processKillAvatar(const QByteArray& datagram) {
void AvatarHashMap::processKillAvatar(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
// read the node id
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID));
QUuid sessionUUID = QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID));
removeAvatar(sessionUUID);
}

View file

@ -2,7 +2,7 @@
// AvatarHashMap.h
// libraries/avatars/src
//
// Created by Stephen AndrewMeadows on 1/28/2014.
// Created by Andrew Meadows on 1/28/2014.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -19,41 +19,41 @@
#include <memory>
#include <DependencyManager.h>
#include <NLPacket.h>
#include <Node.h>
#include <PacketListener.h>
#include "AvatarData.h"
#include <glm/glm.hpp>
class AvatarHashMap : public QObject, public Dependency {
class AvatarHashMap : public QObject, public Dependency, public PacketListener {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
const AvatarHash& getAvatarHash() { return _avatarHash; }
int size() { return _avatarHash.size(); }
public slots:
void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer);
bool isAvatarInRange(const glm::vec3 & position, const float range);
private slots:
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
void processAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processAvatarIdentityPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processAvatarBillboardPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processKillAvatar(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
protected:
AvatarHashMap();
virtual AvatarSharedPointer newSharedAvatar();
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
virtual void removeAvatar(const QUuid& sessionUUID);
AvatarHash _avatarHash;
private:
void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
void processKillAvatar(const QByteArray& datagram);
QUuid _lastOwnerSessionUUID;
};

View file

@ -45,7 +45,7 @@
#include "EntitiesRendererLogging.h"
#include "AddressManager.h"
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices) :
OctreeRenderer(),
_wantScripts(wantScripts),
@ -75,13 +75,13 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
}
EntityTreeRenderer::~EntityTreeRenderer() {
// NOTE: we don't need to delete _entitiesScriptEngine because it is registered with the application and has a
// NOTE: we don't need to delete _entitiesScriptEngine because it is registered with the application and has a
// signal tied to call it's deleteLater on doneRunning
if (_sandboxScriptEngine) {
// TODO: consider reworking how _sandboxScriptEngine is managed. It's treated differently than _entitiesScriptEngine
// because we don't call registerScriptEngineWithApplicationServices() for it. This implementation is confusing and
// potentially error prone because it's not a full fledged ScriptEngine that has been fully connected to the
// application. We did this so that scripts that were ill-formed could be evaluated but not execute against the
// because we don't call registerScriptEngineWithApplicationServices() for it. This implementation is confusing and
// potentially error prone because it's not a full fledged ScriptEngine that has been fully connected to the
// application. We did this so that scripts that were ill-formed could be evaluated but not execute against the
// application services. But this means it's shutdown behavior is different from other ScriptEngines
delete _sandboxScriptEngine;
_sandboxScriptEngine = NULL;
@ -121,7 +121,7 @@ void EntityTreeRenderer::init() {
}
// make sure our "last avatar position" is something other than our current position, so that on our
// first chance, we'll check for enter/leave entity events.
// first chance, we'll check for enter/leave entity events.
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity, Qt::QueuedConnection);
@ -140,7 +140,7 @@ void EntityTreeRenderer::scriptContentsAvailable(const QUrl& url, const QString&
_waitingOnPreload.remove(url);
foreach(EntityItemID entityID, entityIDs) {
checkAndCallPreload(entityID);
}
}
}
}
@ -156,7 +156,7 @@ QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItem
}
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut,
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut,
bool& reload) {
isPending = false;
QUrl url(scriptMaybeURLorText);
@ -355,7 +355,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
// EntityItemIDs from here. The loadEntityScript() method is robust against attempting to load scripts
// for entity IDs that no longer exist.
// for entity IDs that no longer exist.
// for all of our previous containing entities, if they are no longer containing then send them a leave event
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
@ -402,7 +402,7 @@ void EntityTreeRenderer::leaveAllEntities() {
_currentEntitiesInside.clear();
// make sure our "last avatar position" is something other than our current position, so that on our
// first chance, we'll check for enter/leave entity events.
// first chance, we'll check for enter/leave entity events.
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
}
}
@ -515,7 +515,7 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en
const FBXGeometry* result = NULL;
if (entityItem->getType() == EntityTypes::Model) {
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
assert(modelEntityItem); // we need this!!!
Model* model = modelEntityItem->getModel(this);
@ -529,7 +529,7 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en
const Model* EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) {
const Model* result = NULL;
if (entityItem->getType() == EntityTypes::Model) {
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
result = modelEntityItem->getModel(this);
}
@ -540,7 +540,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP
const FBXGeometry* result = NULL;
if (entityItem->getType() == EntityTypes::Model) {
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
if (modelEntityItem->hasCompoundShapeURL()) {
Model* model = modelEntityItem->getModel(this);
@ -680,20 +680,20 @@ float EntityTreeRenderer::getSizeScale() const {
return _viewState->getSizeScale();
}
int EntityTreeRenderer::getBoundaryLevelAdjust() const {
int EntityTreeRenderer::getBoundaryLevelAdjust() const {
return _viewState->getBoundaryLevelAdjust();
}
void EntityTreeRenderer::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
static_cast<EntityTree*>(_tree)->processEraseMessage(dataByteArray, sourceNode);
void EntityTreeRenderer::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) {
static_cast<EntityTree*>(_tree)->processEraseMessage(packet, sourceNode);
}
Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) {
Model* model = NULL;
// Make sure we only create and delete models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection,
QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(Model*, model),
Q_ARG(const QString&, url));
@ -717,7 +717,7 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c
// Before we do any creating or deleting, make sure we're on our renderer thread
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateModel", Qt::BlockingQueuedConnection,
QMetaObject::invokeMethod(this, "updateModel", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(Model*, model),
Q_ARG(Model*, original),
Q_ARG(const QString&, newUrl));
@ -758,7 +758,7 @@ void EntityTreeRenderer::deleteReleasedModels() {
}
}
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking) {
RayToEntityIntersectionResult result;
if (_tree) {
@ -766,8 +766,8 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
OctreeElement* element;
EntityItemPointer intersectedEntity = NULL;
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
(void**)&intersectedEntity, lockType, &result.accurate,
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
(void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
if (result.intersects && intersectedEntity) {
result.entityID = intersectedEntity->getEntityItemID();
@ -780,15 +780,15 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
}
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface,
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface,
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId){
entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
});
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface,
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface,
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
});
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface,
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface,
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
});
@ -968,7 +968,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
} else {
// handle the hover logic...
// if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to
// if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to
// send the hover leave for our previous entity
if (!_currentHoverOverEntityID.isInvalidID()) {
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));

View file

@ -38,7 +38,7 @@ public:
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public ScriptUser {
Q_OBJECT
public:
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices);
virtual ~EntityTreeRenderer();
@ -55,7 +55,7 @@ public:
EntityTree* getTree() { return static_cast<EntityTree*>(_tree); }
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
void processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode);
virtual void init();
virtual void render(RenderArgs* renderArgs) override;
@ -71,7 +71,7 @@ public:
Q_INVOKABLE Model* allocateModel(const QString& url, const QString& collisionUrl);
/// if a renderable entity item needs to update the URL of a model, we will handle that for the entity
Q_INVOKABLE Model* updateModel(Model* original, const QString& newUrl, const QString& collisionUrl);
Q_INVOKABLE Model* updateModel(Model* original, const QString& newUrl, const QString& collisionUrl);
/// if a renderable entity item is done with a model, it should return it to us
void releaseModel(Model* model);
@ -136,7 +136,7 @@ private:
QList<Model*> _releasedModels;
void renderProxies(EntityItemPointer entity, RenderArgs* args);
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking);
EntityItemID _currentHoverOverEntityID;

View file

@ -17,9 +17,18 @@
#include "EntitiesLogging.h"
#include "EntityItem.h"
EntityEditPacketSender::EntityEditPacketSender() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::EntityEditNack, this, "processEntityEditNackPacket");
}
void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<NLPacket> packet) {
if (_shouldNack) {
processNackPacket(QByteArray::fromRawData(packet->getData(), packet->getSizeWithHeader()));
}
}
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType::Value type, QByteArray& buffer, int clockSkew) {
if (type == PacketType::EntityAdd || type == PacketType::EntityEdit) {
EntityItem::adjustEditPacketForClockSkew(buffer, clockSkew);
}

View file

@ -14,12 +14,16 @@
#include <OctreeEditPacketSender.h>
#include <PacketListener.h>
#include "EntityItem.h"
/// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
class EntityEditPacketSender : public OctreeEditPacketSender {
class EntityEditPacketSender : public OctreeEditPacketSender, public PacketListener {
Q_OBJECT
public:
EntityEditPacketSender();
/// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines
/// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in
/// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known.
@ -28,8 +32,16 @@ public:
void queueEraseEntityMessage(const EntityItemID& entityItemID);
void processEntityEditNackPacket(QSharedPointer<NLPacket> packet);
// My server type is the model server
virtual char getMyNodeType() const { return NodeType::EntityServer; }
virtual void adjustEditPacketForClockSkew(PacketType::Value type, QByteArray& buffer, int clockSkew);
public slots:
void toggleNackPackets() { _shouldNack = !_shouldNack; }
private:
bool _shouldNack = false;
};
#endif // hifi_EntityEditPacketSender_h

View file

@ -128,8 +128,8 @@ void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sitting
}
}
bool EntityItemProperties::animationSettingsChanged() const {
return _animationSettingsChanged;
bool EntityItemProperties::animationSettingsChanged() const {
return _animationSettingsChanged;
}
void EntityItemProperties::setAnimationSettings(const QString& value) {
@ -900,11 +900,14 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, Ent
//
// TODO: Implement support for script and visible properties.
//
bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
bool EntityItemProperties::decodeEntityEditPacket(NLPacket& packet, int& processedBytes,
EntityItemID& entityID, EntityItemProperties& properties) {
bool valid = false;
const unsigned char* data = reinterpret_cast<const unsigned char*>(packet.getPayload());
const unsigned char* dataAt = data;
int bytesToRead = packet.getSizeUsed();
processedBytes = 0;
// the first part of the data is an octcode, this is a required element of the edit packet format, but we don't

View file

@ -179,8 +179,8 @@ public:
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
EntityItemID& entityID, EntityItemProperties& properties);
static bool decodeEntityEditPacket(NLPacket& packet, int& processedBytes,
EntityItemID& entityID, EntityItemProperties& properties);
bool glowLevelChanged() const { return _glowLevelChanged; }
bool localRenderAlphaChanged() const { return _localRenderAlphaChanged; }

View file

@ -26,7 +26,7 @@
#include "LogHandler.h"
EntityTree::EntityTree(bool shouldReaverage) :
EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage),
_fbxService(NULL),
_simulation(NULL)
@ -568,8 +568,8 @@ EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entit
return foundEntity;
}
int EntityTree::processEditPacketData(PacketType::Value packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) {
int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
const SharedNodePointer& senderNode) {
if (!getIsServer()) {
qCDebug(entities) << "UNEXPECTED!!! processEditPacketData() should only be called on a server tree.";
@ -578,9 +578,9 @@ int EntityTree::processEditPacketData(PacketType::Value packetType, const unsign
int processedBytes = 0;
// we handle these types of "edit" packets
switch (packetType) {
switch (packet.getType()) {
case PacketType::EntityErase: {
QByteArray dataByteArray((const char*)editData, maxLength);
QByteArray dataByteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(editData), maxLength);
processedBytes = processEraseMessageDetails(dataByteArray, senderNode);
break;
}
@ -598,8 +598,8 @@ int EntityTree::processEditPacketData(PacketType::Value packetType, const unsign
EntityItemID entityItemID;
EntityItemProperties properties;
startDecode = usecTimestampNow();
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength,
processedBytes, entityItemID, properties);
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(packet, processedBytes,
entityItemID, properties);
endDecode = usecTimestampNow();
// If we got a valid edit packet, then it could be a new entity or it could be an update to
@ -609,7 +609,7 @@ int EntityTree::processEditPacketData(PacketType::Value packetType, const unsign
startLookup = usecTimestampNow();
EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID);
endLookup = usecTimestampNow();
if (existingEntity && packetType == PacketType::EntityEdit) {
if (existingEntity && packet.getType() == PacketType::EntityEdit) {
// if the EntityItem exists, then update it
startLogging = usecTimestampNow();
if (wantEditLogging()) {
@ -623,7 +623,7 @@ int EntityTree::processEditPacketData(PacketType::Value packetType, const unsign
existingEntity->markAsChangedOnServer();
endUpdate = usecTimestampNow();
_totalUpdates++;
} else if (packetType == PacketType::EntityAdd) {
} else if (packet.getType() == PacketType::EntityAdd) {
if (senderNode->getCanRez()) {
// this is a new entity... assign a new entityID
properties.setCreated(properties.getLastEdited());
@ -651,7 +651,7 @@ int EntityTree::processEditPacketData(PacketType::Value packetType, const unsign
} else {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex("^Add or Edit failed.*");
qCDebug(entities) << "Add or Edit failed." << packetType << existingEntity.get();
qCDebug(entities) << "Add or Edit failed." << packet.getType() << existingEntity.get();
}
}
@ -854,45 +854,26 @@ void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) {
// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage()
int EntityTree::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) {
lockForWrite();
const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
const unsigned char* dataAt = packetData;
size_t packetLength = dataByteArray.size();
size_t numBytesPacketHeader = numBytesForPacketHeader(dataByteArray);
size_t processedBytes = numBytesPacketHeader;
dataAt += numBytesPacketHeader;
packet.seek(sizeof(OCTREE_PACKET_FLAGS) + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME));
dataAt += sizeof(OCTREE_PACKET_FLAGS);
processedBytes += sizeof(OCTREE_PACKET_FLAGS);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
processedBytes += sizeof(OCTREE_PACKET_SEQUENCE);
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
processedBytes += sizeof(OCTREE_PACKET_SENT_TIME);
uint16_t numberOfIds = 0; // placeholder for now
memcpy(&numberOfIds, dataAt, sizeof(numberOfIds));
dataAt += sizeof(numberOfIds);
processedBytes += sizeof(numberOfIds);
if (numberOfIds > 0) {
uint16_t numberOfIDs = 0; // placeholder for now
packet.readPrimitive(&numberOfIDs);
if (numberOfIDs > 0) {
QSet<EntityItemID> entityItemIDsToDelete;
for (size_t i = 0; i < numberOfIds; i++) {
for (size_t i = 0; i < numberOfIDs; i++) {
if (processedBytes + NUM_BYTES_RFC4122_UUID > packetLength) {
if (NUM_BYTES_RFC4122_UUID > packet.bytesAvailable()) {
qCDebug(entities) << "EntityTree::processEraseMessage().... bailing because not enough bytes in buffer";
break; // bail to prevent buffer overflow
}
QByteArray encodedID = dataByteArray.mid(processedBytes, NUM_BYTES_RFC4122_UUID);
QUuid entityID = QUuid::fromRfc4122(encodedID);
dataAt += encodedID.size();
processedBytes += encodedID.size();
QUuid entityID = QUuid::fromRfc4122(packet.read(NUM_BYTES_RFC4122_UUID));
EntityItemID entityItemID(entityID);
entityItemIDsToDelete << entityItemID;
@ -904,7 +885,7 @@ int EntityTree::processEraseMessage(const QByteArray& dataByteArray, const Share
deleteEntities(entityItemIDsToDelete, true, true);
}
unlock();
return processedBytes;
return packet.pos();
}
// This version skips over the header

View file

@ -66,8 +66,8 @@ public:
virtual bool canProcessVersion(PacketVersion thisVersion) const
{ return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; }
virtual bool handlesEditPacketType(PacketType::Value packetType) const;
virtual int processEditPacketData(PacketType::Value packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode);
virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
const SharedNodePointer& senderNode);
virtual bool rootElementHasData() const { return true; }
@ -133,8 +133,8 @@ public:
bool& hasMore);
void forgetEntitiesDeletedBefore(quint64 sinceTime);
int processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
int processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
int processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode);
int processEraseMessageDetails(const QByteArray& buffer, const SharedNodePointer& sourceNode);
EntityItemFBXService* getFBXService() const { return _fbxService; }
void setFBXService(EntityItemFBXService* service) { _fbxService = service; }
@ -186,9 +186,9 @@ public:
virtual quint64 getAverageLoggingTime() const { return _totalEditMessages == 0 ? 0 : _totalLoggingTime / _totalEditMessages; }
void trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytesRead);
quint64 getAverageEditDeltas() const
quint64 getAverageEditDeltas() const
{ return _totalTrackedEdits == 0 ? 0 : _totalEditDeltas / _totalTrackedEdits; }
quint64 getAverageEditBytes() const
quint64 getAverageEditBytes() const
{ return _totalTrackedEdits == 0 ? 0 : _totalEditBytes / _totalTrackedEdits; }
quint64 getMaxEditDelta() const { return _maxEditDelta; }
quint64 getTotalTrackedEdits() const { return _totalTrackedEdits; }

View file

@ -40,6 +40,6 @@ void EntityTreeHeadlessViewer::update() {
}
}
void EntityTreeHeadlessViewer::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
static_cast<EntityTree*>(_tree)->processEraseMessage(dataByteArray, sourceNode);
void EntityTreeHeadlessViewer::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) {
static_cast<EntityTree*>(_tree)->processEraseMessage(packet, sourceNode);
}

View file

@ -38,7 +38,7 @@ public:
EntityTree* getTree() { return (EntityTree*)_tree; }
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
void processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode);
virtual void init();

View file

@ -60,28 +60,25 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const
}
}
Assignment::Assignment(const QByteArray& packet) :
Assignment::Assignment(NLPacket& packet) :
_pool(),
_location(GlobalLocation),
_payload(),
_walletUUID()
{
PacketType::Value packetType = packetTypeForPacket(packet);
if (packetType == PacketType::RequestAssignment) {
if (packet.getType() == PacketType::RequestAssignment) {
_command = Assignment::RequestCommand;
} else if (packetType == PacketType::CreateAssignment) {
} else if (packet.getType() == PacketType::CreateAssignment) {
_command = Assignment::CreateCommand;
}
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
QDataStream packetStream(&packet);
packetStream >> *this;
}
#ifdef WIN32
#pragma warning(default:4351)
#pragma warning(default:4351)
#endif

View file

@ -59,10 +59,9 @@ public:
void swap(Assignment& otherAssignment);
/// Constructs an Assignment from the data in the buffer
/// \param dataBuffer the source buffer to un-pack the assignment from
/// \param numBytes the number of bytes left to read in the source buffer
Assignment(const QByteArray& packet);
/// Constructs an Assignment from a network packet
/// \param packet the packet to un-pack the assignment from
Assignment(NLPacket& packet);
void setUUID(const QUuid& uuid) { _uuid = uuid; }
const QUuid& getUUID() const { return _uuid; }
@ -86,9 +85,6 @@ public:
const char* getTypeName() const;
// implement parseData to return 0 so we can be a subclass of NodeData
int parseData(const QByteArray& packet) { return 0; }
friend QDebug operator<<(QDebug debug, const Assignment& assignment);
friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
friend QDataStream& operator>>(QDataStream &in, Assignment& assignment);

View file

@ -38,6 +38,12 @@ DomainHandler::DomainHandler(QObject* parent) :
{
// if we get a socket that make sure our NetworkPeer ping timer stops
connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, this, "processICEResponsePacket");
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, this, "processDTLSRequirementPacket");
packetReceiver.registerListener(PacketType::ICEPingReply, this, "processICEPingReplyPacket");
}
void DomainHandler::clearConnectionInfo() {
@ -281,12 +287,28 @@ void DomainHandler::settingsRequestFinished() {
settingsReply->deleteLater();
}
void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) {
// figure out the port that the DS wants us to use for us to talk to them with DTLS
int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket);
void DomainHandler::processICEPingReplyPacket(QSharedPointer<NLPacket> packet) {
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr;
unsigned short dtlsPort = 0;
memcpy(&dtlsPort, dtlsRequirementPacket.data() + numBytesPacketHeader, sizeof(dtlsPort));
if (getIP().isNull()) {
// for now we're unsafely assuming this came back from the domain
if (senderSockAddr == _icePeer.getLocalSocket()) {
qCDebug(networking) << "Connecting to domain using local socket";
activateICELocalSocket();
} else if (senderSockAddr == _icePeer.getPublicSocket()) {
qCDebug(networking) << "Conecting to domain using public socket";
activateICEPublicSocket();
} else {
qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect.";
}
}
}
void DomainHandler::processDTLSRequirementPacket(QSharedPointer<NLPacket> dtlsRequirementPacket) {
// figure out the port that the DS wants us to use for us to talk to them with DTLS
unsigned short dtlsPort;
dtlsRequirementPacket->readPrimitive(&dtlsPort);
qCDebug(networking) << "domain-server DTLS port changed to" << dtlsPort << "- Enabling DTLS.";
@ -295,9 +317,13 @@ void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirement
// initializeDTLSSession();
}
void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) {
QDataStream iceResponseStream(icePacket);
iceResponseStream.skipRawData(numBytesForPacketHeader(icePacket));
void DomainHandler::processICEResponsePacket(QSharedPointer<NLPacket> icePacket) {
if (!_icePeer.hasSockets()) {
// bail on processing this packet if our ice peer doesn't have sockets
return;
}
QDataStream iceResponseStream(icePacket.data());
iceResponseStream >> _icePeer;

View file

@ -21,17 +21,19 @@
#include "HifiSockAddr.h"
#include "NetworkPeer.h"
#include "NLPacket.h"
#include "PacketListener.h"
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;
const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101;
class DomainHandler : public QObject {
class DomainHandler : public QObject, public PacketListener {
Q_OBJECT
public:
DomainHandler(QObject* parent = 0);
void clearConnectionInfo();
void clearSettings();
@ -69,9 +71,7 @@ public:
void requestDomainSettings();
const QJsonObject& getSettingsObject() const { return _settingsObject; }
void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket);
void processICEResponsePacket(const QByteArray& icePacket);
void setPendingPath(const QString& pendingPath) { _pendingPath = pendingPath; }
const QString& getPendingPath() { return _pendingPath; }
void clearPendingPath() { _pendingPath.clear(); }
@ -83,6 +83,10 @@ public slots:
void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT);
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
void processICEPingReplyPacket(QSharedPointer<NLPacket> packet);
void processDTLSRequirementPacket(QSharedPointer<NLPacket> dtlsRequirementPacket);
void processICEResponsePacket(QSharedPointer<NLPacket> icePacket);
private slots:
void completedHostnameLookup(const QHostInfo& hostInfo);
void completedIceServerHostnameLookup();

View file

@ -50,6 +50,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
_localSockAddr(),
_publicSockAddr(),
_stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT),
_packetReceiver(this),
_numCollectedPackets(0),
_numCollectedBytes(0),
_packetStatTimer(),
@ -94,7 +95,14 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
// check the local socket right now
updateLocalSockAddr();
// TODO: Create a new thread, and move PacketReceiver to it
connect(&_nodeSocket, &QUdpSocket::readyRead, &_packetReceiver, &PacketReceiver::processDatagrams);
_packetStatTimer.start();
// make sure we handle STUN response packets
_packetReceiver.registerListener(PacketType::StunResponse, this, "processSTUNResponse");
}
void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {
@ -165,54 +173,33 @@ void LimitedNodeList::changeSocketBufferSizes(int numBytes) {
}
}
bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
PacketType::Value checkType = packetTypeForPacket(packet);
int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());
if (packet[numPacketTypeBytes] != versionForPacketType(checkType)
&& checkType != PacketType::StunResponse) {
PacketType::Value mismatchType = packetTypeForPacket(packet);
static QMultiMap<QUuid, PacketType::Value> versionDebugSuppressMap;
QUuid senderUUID = uuidFromPacketHeader(packet);
if (!versionDebugSuppressMap.contains(senderUUID, checkType)) {
qCDebug(networking) << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender"
<< uuidFromPacketHeader(packet) << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but"
<< qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";
emit packetVersionMismatch();
versionDebugSuppressMap.insert(senderUUID, checkType);
}
return false;
}
if (!NON_VERIFIED_PACKETS.contains(checkType)) {
bool LimitedNodeList::packetSourceAndHashMatch(const NLPacket& packet, SharedNodePointer& matchingNode) {
if (!NON_VERIFIED_PACKETS.contains(packet.getType()) && !NON_SOURCED_PACKETS.contains(packet.getType())) {
// figure out which node this is from
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
if (sendingNode) {
matchingNode = nodeWithUUID(packet.getSourceID());
if (matchingNode) {
// check if the md5 hash in the header matches the hash we would expect
if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {
if (packet.getVerificationHash() == packet.payloadHashWithConnectionUUID(matchingNode->getConnectionSecret())) {
return true;
} else {
static QMultiMap<QUuid, PacketType::Value> hashDebugSuppressMap;
const QUuid& senderID = packet.getSourceID();
QUuid senderUUID = uuidFromPacketHeader(packet);
if (!hashDebugSuppressMap.contains(senderUUID, checkType)) {
qCDebug(networking) << "Packet hash mismatch on" << checkType << "- Sender"
<< uuidFromPacketHeader(packet);
if (!hashDebugSuppressMap.contains(senderID, packet.getType())) {
qCDebug(networking) << "Packet hash mismatch on" << packet.getType() << "- Sender" << senderID;
hashDebugSuppressMap.insert(senderUUID, checkType);
hashDebugSuppressMap.insert(senderID, packet.getType());
}
}
} else {
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ received from unknown node with UUID");
qCDebug(networking) << "Packet of type" << checkType << "received from unknown node with UUID"
<< qPrintable(uuidStringWithoutCurlyBraces(uuidFromPacketHeader(packet)));
qCDebug(networking) << "Packet of type" << packet.getType() << "received from unknown node with UUID"
<< qPrintable(uuidStringWithoutCurlyBraces(packet.getSourceID()));
}
} else {
return true;
@ -317,47 +304,19 @@ PacketSequenceNumber LimitedNodeList::getNextSequenceNumberForPacket(const QUuid
return _packetSequenceNumbers[nodeUUID][packetType]++;
}
void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
// the node decided not to do anything with this packet
// if it comes from a known source we should keep that node alive
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
if (matchingNode) {
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
}
}
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
QMutexLocker locker(&sendingNode->getMutex());
int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray &packet) {
QMutexLocker locker(&matchingNode->getMutex());
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
// if this was a sequence numbered packet we should store the last seq number for
// a packet of this type for this node
PacketType::Value packetType = packetTypeForPacket(packet);
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
matchingNode->setLastSequenceNumberForPacketType(sequenceNumberFromHeader(packet, packetType), packetType);
}
NodeData* linkedData = matchingNode->getLinkedData();
NodeData* linkedData = sendingNode->getLinkedData();
if (!linkedData && linkedDataCreateCallback) {
linkedDataCreateCallback(matchingNode.data());
linkedDataCreateCallback(sendingNode.data());
}
if (linkedData) {
QMutexLocker linkedDataLocker(&linkedData->getMutex());
return linkedData->parseData(packet);
return linkedData->parseData(*packet);
}
return 0;
}
int LimitedNodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
if (matchingNode) {
return updateNodeWithDataFromPacket(matchingNode, packet);
}
// we weren't able to match the sender address to the address we have for this node, unlock and don't parse
return 0;
}
@ -368,13 +327,6 @@ SharedNodePointer LimitedNodeList::nodeWithUUID(const QUuid& nodeUUID) {
return it == _nodeHash.cend() ? SharedNodePointer() : it->second;
}
SharedNodePointer LimitedNodeList::sendingNodeForPacket(const QByteArray& packet) {
QUuid nodeUUID = uuidFromPacketHeader(packet);
// return the matching node, or NULL if there is no match
return nodeWithUUID(nodeUUID);
}
void LimitedNodeList::eraseAllNodes() {
qCDebug(networking) << "Clearing the NodeList. Deleting all nodes in list.";
@ -416,9 +368,9 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
}
}
void LimitedNodeList::processKillNode(const QByteArray& dataByteArray) {
void LimitedNodeList::processKillNode(NLPacket& packet) {
// read the node id
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(dataByteArray), NUM_BYTES_RFC4122_UUID));
QUuid nodeUUID = QUuid::fromRfc4122(packet.read(NUM_BYTES_RFC4122_UUID));
// kill the node with this UUID, if it exists
killNodeWithUUID(nodeUUID);
@ -477,9 +429,8 @@ std::unique_ptr<NLPacket> LimitedNodeList::constructPingPacket(PingType_t pingTy
return pingPacket;
}
std::unique_ptr<NLPacket> LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
QDataStream pingPacketStream(pingPacket);
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
std::unique_ptr<NLPacket> LimitedNodeList::constructPingReplyPacket(NLPacket& pingPacket) {
QDataStream pingPacketStream(&pingPacket);
PingType_t typeFromOriginalPing;
pingPacketStream >> typeFromOriginalPing;
@ -508,11 +459,11 @@ std::unique_ptr<NLPacket> LimitedNodeList::constructICEPingPacket(PingType_t pin
return icePingPacket;
}
std::unique_ptr<NLPacket> LimitedNodeList::constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID) {
std::unique_ptr<NLPacket> LimitedNodeList::constructICEPingReplyPacket(NLPacket& pingPacket, const QUuid& iceID) {
// pull out the ping type so we can reply back with that
PingType_t pingType;
memcpy(&pingType, pingPacket.data() + NUM_BYTES_RFC4122_UUID, sizeof(PingType_t));
memcpy(&pingType, pingPacket.getPayload() + NUM_BYTES_RFC4122_UUID, sizeof(PingType_t));
int packetSize = NUM_BYTES_RFC4122_UUID + sizeof(PingType_t);
auto icePingReplyPacket = NLPacket::create(PacketType::ICEPingReply, packetSize);
@ -635,7 +586,7 @@ void LimitedNodeList::rebindNodeSocket() {
_nodeSocket.bind(QHostAddress::AnyIPv4, oldPort);
}
bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
bool LimitedNodeList::processSTUNResponse(QSharedPointer<NLPacket> packet) {
// check the cookie to make sure this is actually a STUN response
// and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS
const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4;
@ -645,13 +596,13 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
int attributeStartIndex = NUM_BYTES_STUN_HEADER;
if (memcmp(packet.data() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
if (memcmp(packet->getData() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
&RFC_5389_MAGIC_COOKIE_NETWORK_ORDER,
sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) {
// enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE
while (attributeStartIndex < packet.size()) {
if (memcmp(packet.data() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) {
while (attributeStartIndex < packet->getSizeWithHeader()) {
if (memcmp(packet->getData() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) {
const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4;
const int NUM_BYTES_FAMILY_ALIGN = 1;
const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8;
@ -659,14 +610,14 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
uint8_t addressFamily = 0;
memcpy(&addressFamily, packet.data() + byteIndex, sizeof(addressFamily));
memcpy(&addressFamily, packet->getData() + byteIndex, sizeof(addressFamily));
byteIndex += sizeof(addressFamily);
if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) {
// grab the X-Port
uint16_t xorMappedPort = 0;
memcpy(&xorMappedPort, packet.data() + byteIndex, sizeof(xorMappedPort));
memcpy(&xorMappedPort, packet->getData() + byteIndex, sizeof(xorMappedPort));
uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
@ -674,7 +625,7 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
// grab the X-Address
uint32_t xorMappedAddress = 0;
memcpy(&xorMappedAddress, packet.data() + byteIndex, sizeof(xorMappedAddress));
memcpy(&xorMappedAddress, packet->getData() + byteIndex, sizeof(xorMappedAddress));
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
@ -704,7 +655,7 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
const int NUM_BYTES_ATTRIBUTE_TYPE = 2;
uint16_t attributeLength = 0;
memcpy(&attributeLength, packet.data() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
memcpy(&attributeLength, packet->getData() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
sizeof(attributeLength));
attributeLength = ntohs(attributeLength);

View file

@ -27,7 +27,6 @@
#include <QtCore/QReadWriteLock>
#include <QtCore/QSet>
#include <QtCore/QSharedMemory>
#include <QtCore/QSharedPointer>
#include <QtNetwork/QUdpSocket>
#include <QtNetwork/QHostAddress>
@ -39,6 +38,8 @@
#include "Node.h"
#include "NLPacket.h"
#include "udt/PacketHeaders.h"
#include "PacketReceiver.h"
#include "PacketListener.h"
#include "NLPacketList.h"
#include "UUIDHasher.h"
@ -61,9 +62,6 @@ const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
class HifiSockAddr;
typedef QSharedPointer<Node> SharedNodePointer;
Q_DECLARE_METATYPE(SharedNodePointer)
using namespace tbb;
typedef std::pair<QUuid, SharedNodePointer> UUIDNodePair;
typedef concurrent_unordered_map<QUuid, SharedNodePointer, UUIDHasher> NodeHash;
@ -76,7 +74,7 @@ namespace PingType {
const PingType_t Symmetric = 3;
}
class LimitedNodeList : public QObject, public Dependency {
class LimitedNodeList : public QObject, public Dependency, public PacketListener {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
@ -117,9 +115,9 @@ public:
QUdpSocket& getNodeSocket() { return _nodeSocket; }
QUdpSocket& getDTLSSocket();
bool packetVersionAndHashMatch(const QByteArray& packet);
bool packetSourceAndHashMatch(const NLPacket& packet, SharedNodePointer& matchingNode);
qint64 readDatagram(QByteArray& incomingPacket, QHostAddress* address, quint16 * port);
PacketReceiver& getPacketReceiver() { return _packetReceiver; }
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr);
@ -135,7 +133,6 @@ public:
int size() const { return _nodeHash.size(); }
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
SharedNodePointer sendingNodeForPacket(const QByteArray& packet);
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
@ -147,11 +144,9 @@ public:
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; }
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
void processKillNode(const QByteArray& datagram);
void processKillNode(NLPacket& packet);
int updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray& packet);
int findNodeAndUpdateWithDataFromPacket(const QByteArray& packet);
int updateNodeWithDataFromPacket(QSharedPointer<NLPacket> packet, SharedNodePointer matchingNode);
unsigned int broadcastToNodes(std::unique_ptr<NLPacket> packet, const NodeSet& destinationNodeTypes);
SharedNodePointer soloNodeOfType(char nodeType);
@ -160,12 +155,12 @@ public:
void resetPacketStats();
std::unique_ptr<NLPacket> constructPingPacket(PingType_t pingType = PingType::Agnostic);
std::unique_ptr<NLPacket> constructPingReplyPacket(const QByteArray& pingPacket);
std::unique_ptr<NLPacket> constructPingReplyPacket(NLPacket& pingPacket);
std::unique_ptr<NLPacket> constructICEPingPacket(PingType_t pingType, const QUuid& iceID);
std::unique_ptr<NLPacket> constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID);
std::unique_ptr<NLPacket> constructICEPingReplyPacket(NLPacket& pingPacket, const QUuid& iceID);
virtual bool processSTUNResponse(const QByteArray& packet);
virtual bool processSTUNResponse(QSharedPointer<NLPacket> packet);
void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr);
void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID);
@ -234,6 +229,7 @@ public slots:
virtual void sendSTUNRequest();
void killNodeWithUUID(const QUuid& nodeUUID);
signals:
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
void nodeAdded(SharedNodePointer);
@ -245,11 +241,6 @@ signals:
void canAdjustLocksChanged(bool canAdjustLocks);
void canRezChanged(bool canRez);
void dataSent(const quint8 channel_type, const int bytes);
void dataReceived(const quint8 channel_type, const int bytes);
void packetVersionMismatch();
protected:
LimitedNodeList(unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
@ -282,6 +273,8 @@ protected:
HifiSockAddr _publicSockAddr;
HifiSockAddr _stunSockAddr;
PacketReceiver _packetReceiver;
// XXX can BandwidthRecorder be used for this?
int _numCollectedPackets;
int _numCollectedBytes;

View file

@ -13,7 +13,7 @@
qint64 NLPacket::localHeaderSize(PacketType::Value type) {
qint64 size = ((NON_SOURCED_PACKETS.contains(type)) ? 0 : NUM_BYTES_RFC4122_UUID) +
((NON_VERIFIED_PACKETS.contains(type)) ? 0 : NUM_BYTES_RFC4122_UUID);
((NON_SOURCED_PACKETS.contains(type) || NON_VERIFIED_PACKETS.contains(type)) ? 0 : NUM_BYTES_MD5_HASH);
return size;
}
@ -33,6 +33,19 @@ std::unique_ptr<NLPacket> NLPacket::create(PacketType::Value type, qint64 size)
return std::unique_ptr<NLPacket>(new NLPacket(type, size));
}
std::unique_ptr<NLPacket> NLPacket::fromReceivedPacket(std::unique_ptr<char> data, qint64 size,
const HifiSockAddr& senderSockAddr) {
// Fail with null data
Q_ASSERT(data);
// Fail with invalid size
Q_ASSERT(size >= 0);
// allocate memory
return std::unique_ptr<NLPacket>(new NLPacket(std::move(data), size, senderSockAddr));
}
std::unique_ptr<NLPacket> NLPacket::createCopy(const NLPacket& other) {
return std::unique_ptr<NLPacket>(new NLPacket(other));
}
@ -43,15 +56,52 @@ NLPacket::NLPacket(PacketType::Value type, qint64 size) : Packet(type, localHead
NLPacket::NLPacket(const NLPacket& other) : Packet(other) {
}
void NLPacket::setSourceUuid(QUuid sourceUuid) {
Q_ASSERT(!NON_SOURCED_PACKETS.contains(_type));
auto offset = Packet::totalHeadersSize();
memcpy(_packet.get() + offset, sourceUuid.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
NLPacket::NLPacket(std::unique_ptr<char> data, qint64 size, const HifiSockAddr& senderSockAddr) :
Packet(std::move(data), size, senderSockAddr)
{
readSourceID();
readVerificationHash();
}
void NLPacket::setConnectionUuid(QUuid connectionUuid) {
Q_ASSERT(!NON_VERIFIED_PACKETS.contains(_type));
auto offset = Packet::totalHeadersSize() +
((NON_SOURCED_PACKETS.contains(_type)) ? 0 : NUM_BYTES_RFC4122_UUID);
memcpy(_packet.get() + offset, connectionUuid.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
void NLPacket::readSourceID() {
if (!NON_SOURCED_PACKETS.contains(_type)) {
auto offset = Packet::totalHeadersSize();
_sourceID = QUuid::fromRfc4122(QByteArray::fromRawData(_packet.get() + offset, NUM_BYTES_RFC4122_UUID));
}
}
void NLPacket::readVerificationHash() {
if (!NON_SOURCED_PACKETS.contains(_type) && !NON_VERIFIED_PACKETS.contains(_type)) {
auto offset = Packet::totalHeadersSize() + NUM_BYTES_RFC4122_UUID;
_verificationHash = QByteArray(_packet.get() + offset, NUM_BYTES_MD5_HASH);
}
}
void NLPacket::setSourceID(const QUuid& sourceID) {
Q_ASSERT(!NON_SOURCED_PACKETS.contains(_type));
auto offset = Packet::totalHeadersSize();
memcpy(_packet.get() + offset, sourceID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID);
_sourceID = sourceID;
}
void NLPacket::setVerificationHash(const QByteArray& verificationHash) {
Q_ASSERT(!NON_SOURCED_PACKETS.contains(_type) && !NON_VERIFIED_PACKETS.contains(_type));
auto offset = Packet::totalHeadersSize() + NUM_BYTES_RFC4122_UUID;
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
_verificationHash = verificationHash;
}
QByteArray NLPacket::payloadHashWithConnectionUUID(const QUuid& connectionUUID) const {
QCryptographicHash hash(QCryptographicHash::Md5);
// add the packet payload and the connection UUID
hash.addData(_payloadStart, _sizeUsed);
hash.addData(connectionUUID.toRfc4122());
// return the hash
return hash.result();
}

View file

@ -18,6 +18,8 @@ class NLPacket : public Packet {
Q_OBJECT
public:
static std::unique_ptr<NLPacket> create(PacketType::Value type, qint64 size = -1);
static std::unique_ptr<NLPacket> fromReceivedPacket(std::unique_ptr<char> data, qint64 size,
const HifiSockAddr& senderSockAddr);
// Provided for convenience, try to limit use
static std::unique_ptr<NLPacket> createCopy(const NLPacket& other);
@ -27,12 +29,24 @@ public:
virtual qint64 totalHeadersSize() const; // Cumulated size of all the headers
virtual qint64 localHeaderSize() const; // Current level's header size
const QUuid& getSourceID() const { return _sourceID; }
const QByteArray& getVerificationHash() const { return _verificationHash; }
QByteArray payloadHashWithConnectionUUID(const QUuid& connectionUUID) const;
protected:
NLPacket(PacketType::Value type, qint64 size);
NLPacket(std::unique_ptr<char> data, qint64 size, const HifiSockAddr& senderSockAddr);
NLPacket(const NLPacket& other);
void setSourceUuid(QUuid sourceUuid);
void setConnectionUuid(QUuid connectionUuid);
void readSourceID();
void setSourceID(const QUuid& sourceID);
void readVerificationHash();
void setVerificationHash(const QByteArray& verificationHash);
QUuid _sourceID;
QByteArray _verificationHash;
};
#endif // hifi_NLPacket_h

View file

@ -59,8 +59,8 @@ public:
quint64 getWakeTimestamp() const { return _wakeTimestamp; }
void setWakeTimestamp(quint64 wakeTimestamp) { _wakeTimestamp = wakeTimestamp; }
quint64 getLastHeardMicrostamp() const { return _lastHeardMicrostamp; }
void setLastHeardMicrostamp(quint64 lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
quint64 getLastHeardMicrostamp() const { return _lastHeardMicrostamp.load(); }
void setLastHeardMicrostamp(quint64 lastHeardMicrostamp) { _lastHeardMicrostamp.store(lastHeardMicrostamp); }
QByteArray toByteArray() const;
@ -92,7 +92,7 @@ protected:
HifiSockAddr* _activeSocket;
quint64 _wakeTimestamp;
quint64 _lastHeardMicrostamp;
std::atomic_ullong _lastHeardMicrostamp;
QTimer* _pingTimer = NULL;

View file

@ -17,8 +17,8 @@
#include <QtCore/QDebug>
#include <QtCore/QMutex>
#include <QtCore/QSharedPointer>
#include <QtCore/QUuid>
#include <QMutex>
#include "HifiSockAddr.h"
#include "NetworkPeer.h"
@ -92,6 +92,9 @@ private:
PacketTypeSequenceMap _lastSequenceNumbers;
};
typedef QSharedPointer<Node> SharedNodePointer;
Q_DECLARE_METATYPE(SharedNodePointer)
QDebug operator<<(QDebug debug, const Node &message);
#endif // hifi_Node_h

View file

@ -14,6 +14,9 @@
#include <QtCore/QMutex>
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include "NLPacket.h"
class Node;
@ -22,7 +25,7 @@ class NodeData : public QObject {
public:
NodeData();
virtual ~NodeData() = 0;
virtual int parseData(const QByteArray& packet) = 0;
virtual int parseData(NLPacket& packet) { return 0; }
QMutex& getMutex() { return _mutex; }

View file

@ -45,6 +45,11 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
qRegisterMetaType<SharedNodePointer>();
firstCall = false;
}
setCustomDeleter([](Dependency* dependency){
static_cast<NodeList*>(dependency)->deleteLater();
});
auto addressManager = DependencyManager::get<AddressManager>();
// handle domain change signals from AddressManager
@ -90,6 +95,12 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
// we definitely want STUN to update our public socket, so call the LNL to kick that off
startSTUNPublicSocketUpdate();
auto& packetReceiver = getPacketReceiver();
packetReceiver.registerListener(PacketType::DomainList, this, "processDomainServerList");
packetReceiver.registerListener(PacketType::Ping, this, "processPingPacket");
packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket");
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
}
qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) {
@ -116,9 +127,8 @@ qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) {
return sendStats(statsObject, _domainHandler.getSockAddr());
}
void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) {
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
void NodeList::timePingReply(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendingNode) {
QDataStream packetStream(packet.data());
quint8 pingType;
quint64 ourOriginalTime, othersReplyTime;
@ -152,106 +162,36 @@ void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer&
}
}
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
PacketType::Value packetType = packetTypeForPacket(packet);
switch (packetType) {
case PacketType::DomainList:
case PacketType::DomainServerAddedNode: {
if (!_domainHandler.getSockAddr().isNull()) {
// only process a packet from domain-server if we're talking to a domain
// TODO: how do we make sure this is actually the domain we want the list from (DTLS probably)
if (packetType == PacketType::DomainList) {
processDomainServerList(packet);
} else if (packetType == PacketType::DomainServerAddedNode) {
processDomainServerAddedNode(packet);
}
}
break;
}
case PacketType::DomainServerRequireDTLS: {
_domainHandler.parseDTLSRequirementPacket(packet);
break;
}
case PacketType::ICEServerPeerInformation: {
if (!_domainHandler.getICEPeer().hasSockets()) {
_domainHandler.processICEResponsePacket(packet);
}
break;
}
case PacketType::Ping: {
// send back a reply
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
if (matchingNode) {
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
auto replyPacket = constructPingReplyPacket(packet);
sendPacket(std::move(replyPacket), *matchingNode, senderSockAddr);
void NodeList::processPingPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
// send back a reply
auto replyPacket = constructPingReplyPacket(*packet);
const HifiSockAddr& senderSockAddr = packet->getSenderSockAddr();
sendPacket(std::move(replyPacket), sendingNode, senderSockAddr);
// If we don't have a symmetric socket for this node and this socket doesn't match
// what we have for public and local then set it as the symmetric.
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
if (matchingNode->getSymmetricSocket().isNull()) {
if (senderSockAddr != matchingNode->getLocalSocket() && senderSockAddr != matchingNode->getPublicSocket()) {
matchingNode->setSymmetricSocket(senderSockAddr);
}
}
}
break;
// If we don't have a symmetric socket for this node and this socket doesn't match
// what we have for public and local then set it as the symmetric.
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
if (sendingNode->getSymmetricSocket().isNull()) {
if (senderSockAddr != sendingNode->getLocalSocket() && senderSockAddr != sendingNode->getPublicSocket()) {
sendingNode->setSymmetricSocket(senderSockAddr);
}
case PacketType::PingReply: {
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
if (sendingNode) {
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
// activate the appropriate socket for this node, if not yet updated
activateSocketFromNodeCommunication(packet, sendingNode);
// set the ping time for this node for stat collection
timePingReply(packet, sendingNode);
}
break;
}
case PacketType::ICEPing: {
// send back a reply
auto replyPacket = constructICEPingReplyPacket(packet, _domainHandler.getICEClientID());
sendPacket(std::move(replyPacket), senderSockAddr);
break;
}
case PacketType::ICEPingReply: {
qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr;
if (_domainHandler.getIP().isNull()) {
// for now we're unsafely assuming this came back from the domain
if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
qCDebug(networking) << "Connecting to domain using local socket";
_domainHandler.activateICELocalSocket();
} else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) {
qCDebug(networking) << "Conecting to domain using public socket";
_domainHandler.activateICEPublicSocket();
} else {
qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect.";
}
}
}
case PacketType::StunResponse: {
// a STUN packet begins with 00, we've checked the second zero with packetVersionMatch
// pass it along so it can be processed into our public address and port
processSTUNResponse(packet);
break;
}
case PacketType::DomainServerPathResponse: {
handleDSPathQueryResponse(packet);
break;
}
default:
LimitedNodeList::processNodeData(senderSockAddr, packet);
break;
}
}
void NodeList::processPingReplyPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
// activate the appropriate socket for this node, if not yet updated
activateSocketFromNodeCommunication(packet, sendingNode);
// set the ping time for this node for stat collection
timePingReply(packet, sendingNode);
}
void NodeList::processICEPingPacket(QSharedPointer<NLPacket> packet) {
// send back a reply
auto replyPacket = constructICEPingReplyPacket(*packet, _domainHandler.getICEClientID());
sendPacket(std::move(replyPacket), packet->getSenderSockAddr());
}
void NodeList::reset() {
LimitedNodeList::reset();
@ -419,43 +359,35 @@ void NodeList::sendDSPathQuery(const QString& newPath) {
}
}
void NodeList::handleDSPathQueryResponse(const QByteArray& packet) {
void NodeList::processDomainServerPathQueryResponse(QSharedPointer<NLPacket> packet) {
// This is a response to a path query we theoretically made.
// In the future we may want to check that this was actually from our DS and for a query we actually made.
int numHeaderBytes = numBytesForPacketHeaderGivenPacketType(PacketType::DomainServerPathResponse);
const char* startPosition = packet.data() + numHeaderBytes;
const char* currentPosition = startPosition;
// figure out how many bytes the path query is
qint16 numPathBytes;
memcpy(&numPathBytes, currentPosition, sizeof(numPathBytes));
currentPosition += sizeof(numPathBytes);
packet->readPrimitive(&numPathBytes);
// make sure it is safe to pull the path
if (numPathBytes <= packet.size() - numHeaderBytes - (currentPosition - startPosition)) {
// pull the path from the packet
QString pathQuery = QString::fromUtf8(currentPosition, numPathBytes);
currentPosition += numPathBytes;
// pull the path from the packet
QString pathQuery = QString::fromUtf8(packet->read(numPathBytes));
// figure out how many bytes the viewpoint is
qint16 numViewpointBytes;
memcpy(&numViewpointBytes, currentPosition, sizeof(numViewpointBytes));
currentPosition += sizeof(numViewpointBytes);
// figure out how many bytes the viewpoint is
qint16 numViewpointBytes;
packet->readPrimitive(&numViewpointBytes);
// make sure it is safe to pull the viewpoint
if (numViewpointBytes <= packet.size() - numHeaderBytes - (currentPosition - startPosition)) {
// pull the viewpoint from the packet
QString viewpoint = QString::fromUtf8(currentPosition, numViewpointBytes);
// pull the viewpoint from the packet
auto stringData = packet->read(numViewpointBytes);
if (stringData.size() == numViewpointBytes) {
QString viewpoint = QString::fromUtf8(stringData);
// Hand it off to the AddressManager so it can handle it as a relative viewpoint
if (DependencyManager::get<AddressManager>()->goToViewpointForPath(viewpoint, pathQuery)) {
qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery;
} else {
qCDebug(networking) << "Could not go to viewpoint" << viewpoint
<< "which was the lookup result for path" << pathQuery;
}
// Hand it off to the AddressManager so it can handle it as a relative viewpoint
if (DependencyManager::get<AddressManager>()->goToViewpointForPath(viewpoint, pathQuery)) {
qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery;
} else {
qCDebug(networking) << "Could not go to viewpoint" << viewpoint
<< "which was the lookup result for path" << pathQuery;
}
} else {
qCDebug(networking) << "Error loading viewpoint from path query response";
}
}
@ -510,49 +442,51 @@ void NodeList::pingPunchForDomainServer() {
}
}
int NodeList::processDomainServerList(const QByteArray& packet) {
void NodeList::processDomainServerList(QSharedPointer<NLPacket> packet) {
if (_domainHandler.getSockAddr().isNull()) {
// refuse to process this packet if we aren't currently connected to the DS
return;
}
// this is a packet from the domain server, reset the count of un-replied check-ins
_numNoReplyDomainCheckIns = 0;
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList);
QDataStream packetStream(packet.data());
// grab the domain's ID from the beginning of the packet
QUuid domainUUID;
packetStream >> domainUUID;
// if this was the first domain-server list from this domain, we've now connected
if (!_domainHandler.isConnected()) {
_domainHandler.setUUID(uuidFromPacketHeader(packet));
_domainHandler.setUUID(domainUUID);
_domainHandler.setIsConnected(true);
}
int readNodes = 0;
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
// pull our owner UUID from the packet, it's always the first thing
QUuid newUUID;
packetStream >> newUUID;
setSessionUUID(newUUID);
// TODO: when fixing this read these are actually chars now, not bools
bool thisNodeCanAdjustLocks;
quint8 thisNodeCanAdjustLocks;
packetStream >> thisNodeCanAdjustLocks;
setThisNodeCanAdjustLocks(thisNodeCanAdjustLocks);
setThisNodeCanAdjustLocks((bool) thisNodeCanAdjustLocks);
bool thisNodeCanRez;
quint8 thisNodeCanRez;
packetStream >> thisNodeCanRez;
setThisNodeCanRez(thisNodeCanRez);
setThisNodeCanRez((bool) thisNodeCanRez);
// pull each node in the packet
while (packetStream.device()->pos() < packet.size()) {
while (packetStream.device()->pos() < packet->getSizeUsed()) {
parseNodeFromPacketStream(packetStream);
}
return readNodes;
}
void NodeList::processDomainServerAddedNode(const QByteArray& packet) {
// setup a QDataStream, skip the header
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
void NodeList::processDomainServerAddedNode(QSharedPointer<NLPacket> packet) {
// setup a QDataStream
QDataStream packetStream(packet.data());
// use our shared method to pull out the new node
parseNodeFromPacketStream(packetStream);
@ -646,10 +580,9 @@ void NodeList::handleNodePingTimeout() {
}
}
void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode) {
void NodeList::activateSocketFromNodeCommunication(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendingNode) {
// deconstruct this ping packet to see if it is a public or local reply
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
QDataStream packetStream(packet.data());
quint8 pingType;
packetStream >> pingType;

View file

@ -38,6 +38,7 @@ const quint64 DOMAIN_SERVER_CHECK_IN_MSECS = 1 * 1000;
const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
using NodePacketPair = std::pair<SharedNodePointer, std::unique_ptr<NLPacket>>;
using NodeSharedPacketPair = std::pair<SharedNodePointer, QSharedPointer<NLPacket>>;
class Application;
class Assignment;
@ -61,7 +62,7 @@ public:
void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
void resetNodeInterestSet() { _nodeTypesOfInterest.clear(); }
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
void processReceivedPacket(std::unique_ptr<NLPacket>, HifiSockAddr senderSockAddr);
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
void sendAssignment(Assignment& assignment);
@ -70,6 +71,15 @@ public slots:
void reset();
void sendDomainServerCheckIn();
void handleDSPathQuery(const QString& newPath);
void processDomainServerList(QSharedPointer<NLPacket> packet);
void processDomainServerAddedNode(QSharedPointer<NLPacket> packet);
void processDomainServerPathQueryResponse(QSharedPointer<NLPacket> packet);
void processPingPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processPingReplyPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processICEPingPacket(QSharedPointer<NLPacket> packet);
signals:
void limitOfSilentDomainCheckInsReached();
private slots:
@ -88,15 +98,11 @@ private:
void processDomainServerAuthRequest(const QByteArray& packet);
void requestAuthForDomainServer();
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
void handleDSPathQueryResponse(const QByteArray& packet);
void activateSocketFromNodeCommunication(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendingNode);
void timePingReply(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendingNode);
void sendDSPathQuery(const QString& newPath);
int processDomainServerList(const QByteArray& packet);
void processDomainServerAddedNode(const QByteArray& packet);
void parseNodeFromPacketStream(QDataStream& packetStream);
void pingPunchForInactiveNode(const SharedNodePointer& node);
@ -106,8 +112,6 @@ private:
DomainHandler _domainHandler;
int _numNoReplyDomainCheckIns;
HifiSockAddr _assignmentServerSocket;
friend class Application;
};
#endif // hifi_NodeList_h

View file

@ -0,0 +1,18 @@
//
// PacketListener.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 07/14/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 "PacketListener.h"
#include "NodeList.h"
PacketListener::~PacketListener() {
DependencyManager::get<NodeList>()->getPacketReceiver().unregisterListener(this);
}

View file

@ -0,0 +1,22 @@
//
// PacketListener.h
// libraries/networking/src
//
// Created by Stephen Birarda on 07/14/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
//
#ifndef hifi_PacketListener_h
#define hifi_PacketListener_h
#pragma once
class PacketListener {
public:
virtual ~PacketListener();
};
#endif // hifi_PacketListener_h

View file

@ -0,0 +1,272 @@
//
// PacketReceiver.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 1/23/2014.
// Update by Ryan Huffman on 7/8/2015.
// 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 "PacketReceiver.h"
#include "DependencyManager.h"
#include "NetworkLogging.h"
#include "NodeList.h"
#include "SharedUtil.h"
PacketReceiver::PacketReceiver(QObject* parent) :
QObject(parent),
_packetListenerMap()
{
}
void PacketReceiver::registerListenerForTypes(const QSet<PacketType::Value>& types, PacketListener* listener, const char* slot) {
QSet<PacketType::Value> nonSourcedTypes;
QSet<PacketType::Value> sourcedTypes;
foreach(PacketType::Value type, types) {
if (NON_SOURCED_PACKETS.contains(type)) {
nonSourcedTypes << type;
} else {
sourcedTypes << type;
}
}
QObject* object = dynamic_cast<QObject*>(listener);
Q_ASSERT(object);
if (nonSourcedTypes.size() > 0) {
QMetaMethod nonSourcedMethod = matchingMethodForListener(*nonSourcedTypes.begin(), object, slot);
foreach(PacketType::Value type, nonSourcedTypes) {
registerVerifiedListener(type, object, nonSourcedMethod);
}
}
if (sourcedTypes.size() > 0) {
QMetaMethod sourcedMethod = matchingMethodForListener(*sourcedTypes.begin(), object, slot);
foreach(PacketType::Value type, sourcedTypes) {
registerVerifiedListener(type, object, sourcedMethod);
}
}
}
void PacketReceiver::registerListener(PacketType::Value type, PacketListener* listener, const char* slot) {
QObject* object = dynamic_cast<QObject*>(listener);
Q_ASSERT(object);
QMetaMethod matchingMethod = matchingMethodForListener(type, object, slot);
if (matchingMethod.isValid()) {
registerVerifiedListener(type, object, matchingMethod);
}
}
QMetaMethod PacketReceiver::matchingMethodForListener(PacketType::Value type, QObject* object, const char* slot) const {
Q_ASSERT(object);
// normalize the slot with the expected parameters
const QString NON_SOURCED_PACKET_LISTENER_PARAMETERS = "QSharedPointer<NLPacket>";
QSet<QString> possibleSignatures { QString("%1(%2)").arg(slot).arg(NON_SOURCED_PACKET_LISTENER_PARAMETERS) };
if (!NON_SOURCED_PACKETS.contains(type)) {
const QString SOURCED_PACKET_LISTENER_PARAMETERS = "QSharedPointer<NLPacket>,QSharedPointer<Node>";
// a sourced packet must take the shared pointer to the packet but optionally could include
// a shared pointer to the node
possibleSignatures << QString("%1(%2)").arg(slot).arg(SOURCED_PACKET_LISTENER_PARAMETERS);
}
int methodIndex = -1;
foreach(const QString& signature, possibleSignatures) {
QByteArray normalizedSlot =
QMetaObject::normalizedSignature(signature.toStdString().c_str());
// does the constructed normalized method exist?
methodIndex = object->metaObject()->indexOfSlot(normalizedSlot.toStdString().c_str());
break;
}
if (methodIndex < 0) {
qDebug() << "PacketReceiver::registerListener expected a method with one of the following signatures:"
<< possibleSignatures << "- but such a method was not found.";
}
Q_ASSERT(methodIndex >= 0);
// return the converted QMetaMethod
if (methodIndex >= 0) {
return object->metaObject()->method(methodIndex);
} else {
// if somehow (scripting?) something bad gets in here at runtime that doesn't hit the asserts above
// return a non-valid QMetaMethod
return QMetaMethod();
}
}
void PacketReceiver::registerVerifiedListener(PacketType::Value type, QObject* object, const QMetaMethod& slot) {
_packetListenerLock.lock();
if (_packetListenerMap.contains(type)) {
qDebug() << "Warning: Registering a packet listener for packet type " << type
<< "that will remove a previously registered listener";
}
// add the mapping
_packetListenerMap[type] = ObjectMethodPair(object, slot);
_packetListenerLock.unlock();
}
void PacketReceiver::unregisterListener(PacketListener* listener) {
_packetListenerLock.lock();
auto it = _packetListenerMap.begin();
while (it != _packetListenerMap.end()) {
if (it.value().first == dynamic_cast<QObject*>(listener)) {
// this listener matches - erase it
it = _packetListenerMap.erase(it);
} else {
++it;
}
}
_packetListenerLock.unlock();
}
bool PacketReceiver::packetVersionMatch(const NLPacket& packet) {
if (packet.getVersion() != versionForPacketType(packet.getType())
&& packet.getType() != PacketType::StunResponse) {
static QMultiMap<QUuid, PacketType::Value> versionDebugSuppressMap;
const QUuid& senderID = packet.getSourceID();
if (!versionDebugSuppressMap.contains(senderID, packet.getType())) {
qCDebug(networking) << "Packet version mismatch on" << packet.getType() << "- Sender"
<< senderID << "sent" << qPrintable(QString::number(packet.getVersion())) << "but"
<< qPrintable(QString::number(versionForPacketType(packet.getType()))) << "expected.";
emit packetVersionMismatch(packet.getType());
versionDebugSuppressMap.insert(senderID, packet.getType());
}
return false;
} else {
return true;
}
}
void PacketReceiver::processDatagrams() {
//PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
//"PacketReceiver::processDatagrams()");
auto nodeList = DependencyManager::get<NodeList>();
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
// setup a buffer to read the packet into
int packetSizeWithHeader = nodeList->getNodeSocket().pendingDatagramSize();
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[packetSizeWithHeader]);
// if we're supposed to drop this packet then break out here
if (_shouldDropPackets) {
break;
}
// setup a HifiSockAddr to read into
HifiSockAddr senderSockAddr;
// pull the datagram
nodeList->getNodeSocket().readDatagram(buffer.get(), packetSizeWithHeader,
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
// setup an NLPacket from the data we just read
auto packet = NLPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
_inPacketCount++;
_inByteCount += packetSizeWithHeader;
if (packetVersionMatch(*packet)) {
SharedNodePointer matchingNode;
if (nodeList->packetSourceAndHashMatch(*packet, matchingNode)) {
if (matchingNode) {
// No matter if this packet is handled or not, we update the timestamp for the last time we heard
// from this sending node
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
}
_packetListenerLock.lock();
auto it = _packetListenerMap.find(packet->getType());
if (it != _packetListenerMap.end()) {
auto listener = it.value();
if (listener.first) {
bool success = false;
if (matchingNode) {
// if this was a sequence numbered packet we should store the last seq number for
// a packet of this type for this node
if (SEQUENCE_NUMBERED_PACKETS.contains(packet->getType())) {
matchingNode->setLastSequenceNumberForPacketType(packet->readSequenceNumber(), packet->getType());
}
emit dataReceived(matchingNode->getType(), packet->getSizeWithHeader());
QMetaMethod metaMethod = listener.second;
static const QByteArray SHARED_NODE_NORMALIZED = QMetaObject::normalizedType("QSharedPointer<Node>");
if (metaMethod.parameterTypes().contains(SHARED_NODE_NORMALIZED)) {
success = metaMethod.invoke(listener.first,
Q_ARG(QSharedPointer<NLPacket>, QSharedPointer<NLPacket>(packet.release())),
Q_ARG(SharedNodePointer, matchingNode));
} else {
success = metaMethod.invoke(listener.first,
Q_ARG(QSharedPointer<NLPacket>, QSharedPointer<NLPacket>(packet.release())));
}
} else {
emit dataReceived(NodeType::Unassigned, packet->getSizeWithHeader());
success = listener.second.invoke(listener.first,
Q_ARG(QSharedPointer<NLPacket>, QSharedPointer<NLPacket>(packet.release())));
}
if (!success) {
qDebug() << "Error delivering packet " << nameForPacketType(packet->getType()) << " to listener: "
<< listener.first->objectName() << "::" << listener.second.name();
}
} else {
// we have a dead listener - remove this mapping from the _packetListenerMap
qDebug() << "Listener for packet type" << nameForPacketType(packet->getType())
<< "has been destroyed - removing mapping.";
_packetListenerMap.erase(it);
}
} else {
qDebug() << "No listener found for packet type " << nameForPacketType(packet->getType());
}
_packetListenerLock.unlock();
}
}
}
}

View file

@ -0,0 +1,69 @@
//
// PacketReceiver.h
// libraries/networking/src
//
// Created by Stephen Birarda on 1/23/2014.
// Update by Ryan Huffman on 7/8/2015.
// 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_PacketReceiver_h
#define hifi_PacketReceiver_h
#include <QtCore/QMap>
#include <QtCore/QMetaMethod>
#include <QtCore/QMutex>
#include <QtCore/QObject>
#include <QtCore/QSet>
#include "NLPacket.h"
#include "PacketHeaders.h"
class PacketListener;
class PacketReceiver : public QObject {
Q_OBJECT
public:
PacketReceiver(QObject* parent = 0);
PacketReceiver(const PacketReceiver&) = delete;
PacketReceiver& operator=(const PacketReceiver&) = delete;
int getInPacketCount() const { return _inPacketCount; }
int getInByteCount() const { return _inByteCount; }
void setShouldDropPackets(bool shouldDropPackets) { _shouldDropPackets = shouldDropPackets; }
void resetCounters() { _inPacketCount = 0; _inByteCount = 0; }
void registerListenerForTypes(const QSet<PacketType::Value>& types, PacketListener* listener, const char* slot);
void registerListener(PacketType::Value type, PacketListener* listener, const char* slot);
void unregisterListener(PacketListener* listener);
public slots:
void processDatagrams();
signals:
void dataSent(quint8 channelType, int bytes);
void dataReceived(quint8 channelType, int bytes);
void packetVersionMismatch(PacketType::Value type);
private:
bool packetVersionMatch(const NLPacket& packet);
QMetaMethod matchingMethodForListener(PacketType::Value type, QObject* object, const char* slot) const;
void registerVerifiedListener(PacketType::Value type, QObject* listener, const QMetaMethod& slot);
using ObjectMethodPair = std::pair<QObject*, QMetaMethod>;
QMutex _packetListenerLock;
QHash<PacketType::Value, ObjectMethodPair> _packetListenerMap;
int _inPacketCount = 0;
int _inByteCount = 0;
bool _shouldDropPackets = false;
};
#endif // hifi_PacketReceiver_h

View file

@ -24,15 +24,9 @@ void ReceivedPacketProcessor::terminating() {
_hasPackets.wakeAll();
}
void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
// Make sure our Node and NodeList knows we've heard from this node.
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
// TODO: fix the NodePacketPair once we've figured out receive API
NodePacketPair networkPacket(sendingNode, NLPacket::create(PacketType::OctreeStats));
void ReceivedPacketProcessor::queueReceivedPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
lock();
_packets.push_back(std::move(networkPacket));
_packets.push_back({ sendingNode, packet });
_nodePacketCounts[sendingNode->getUUID()]++;
_lastWindowIncomingPackets++;
unlock();
@ -73,13 +67,12 @@ bool ReceivedPacketProcessor::process() {
}
lock();
std::list<NodePacketPair> currentPackets;
std::list<NodeSharedPacketPair> currentPackets;
currentPackets.swap(_packets);
unlock();
for(auto& packetPair : currentPackets) {
// TODO: Replace QByteArray() once NLPacket is coming through on receive side
processPacket(packetPair.first, QByteArray());
processPacket(packetPair.second, packetPair.first);
_lastWindowProcessedPackets++;
midProcess();
}

View file

@ -23,7 +23,7 @@ public:
ReceivedPacketProcessor();
/// Add packet from network receive thread to the processing queue.
void queueReceivedPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
void queueReceivedPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
/// Are there received packets waiting to be processed
bool hasPacketsToProcess() const { return _packets.size() > 0; }
@ -58,7 +58,7 @@ protected:
/// Callback for processing of recieved packets. Implement this to process the incoming packets.
/// \param SharedNodePointer& sendingNode the node that sent this packet
/// \param QByteArray& the packet to be processed
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) = 0;
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) = 0;
/// Implements generic processing behavior for this thread.
virtual bool process();
@ -76,7 +76,7 @@ protected:
virtual void postProcess() { }
protected:
std::list<NodePacketPair> _packets;
std::list<NodeSharedPacketPair> _packets;
QHash<QUuid, int> _nodePacketCounts;
QWaitCondition _hasPackets;

View file

@ -13,8 +13,9 @@
#include <stdint.h>
#include <qbytearray.h>
#include "RingBufferHistory.h"
#include "NLPacket.h"
#include "RingBufferHistory.h"
#include "SequenceNumberStats.h"
class NLPacket;

View file

@ -18,10 +18,10 @@
#include "ThreadedAssignment.h"
ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) :
ThreadedAssignment::ThreadedAssignment(NLPacket& packet) :
Assignment(packet),
_isFinished(false),
_datagramProcessingThread(NULL)
_isFinished(false)
{
}
@ -34,6 +34,14 @@ void ThreadedAssignment::setFinished(bool isFinished) {
qDebug() << "ThreadedAssignment::setFinished(true) called - finishing up.";
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
// we should de-register immediately for any of our packets
packetReceiver.unregisterListener(this);
// we should also tell the packet receiver to drop packets while we're cleaning up
packetReceiver.setShouldDropPackets(true);
if (_domainServerTimer) {
_domainServerTimer->stop();
}
@ -42,31 +50,9 @@ void ThreadedAssignment::setFinished(bool isFinished) {
_statsTimer->stop();
}
// stop processing datagrams from the node socket
// this ensures we won't process a domain list while we are going down
auto nodeList = DependencyManager::get<NodeList>();
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
// call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup
aboutToFinish();
// if we have a datagram processing thread, quit it and wait on it to make sure that
// the node socket is back on the same thread as the NodeList
if (_datagramProcessingThread) {
// tell the datagram processing thread to quit and wait until it is done,
// then return the node socket to the NodeList
_datagramProcessingThread->quit();
_datagramProcessingThread->wait();
// set node socket parent back to NodeList
nodeList->getNodeSocket().setParent(nodeList.data());
}
// move the NodeList back to the QCoreApplication instance's thread
nodeList->moveToThread(QCoreApplication::instance()->thread());
emit finished();
}
}
@ -79,9 +65,6 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy
auto nodeList = DependencyManager::get<NodeList>();
nodeList->setOwnerType(nodeType);
// this is a temp fix for Qt 5.3 - rebinding the node socket gives us readyRead for the socket on this thread
nodeList->rebindNodeSocket();
_domainServerTimer = new QTimer();
connect(_domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
_domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
@ -120,16 +103,3 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() {
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
}
}
bool ThreadedAssignment::readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr) {
auto nodeList = DependencyManager::get<NodeList>();
if (nodeList->getNodeSocket().hasPendingDatagrams()) {
destinationByteArray.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(destinationByteArray.data(), destinationByteArray.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
return true;
} else {
return false;
}
}

View file

@ -1,6 +1,6 @@
//
// ThreadedAssignment.h
// libraries/shared/src
// libraries/networking/src
//
// Created by Stephen Birarda on 12/3/2013.
// Copyright 2013 High Fidelity, Inc.
@ -14,12 +14,14 @@
#include <QtCore/QSharedPointer>
#include "PacketListener.h"
#include "Assignment.h"
class ThreadedAssignment : public Assignment {
class ThreadedAssignment : public Assignment, public PacketListener {
Q_OBJECT
public:
ThreadedAssignment(const QByteArray& packet);
ThreadedAssignment(NLPacket& packet);
~ThreadedAssignment() { stop(); }
void setFinished(bool isFinished);
@ -30,17 +32,14 @@ public slots:
/// threaded run of assignment
virtual void run() = 0;
Q_INVOKABLE virtual void stop() { setFinished(true); }
virtual void readPendingDatagrams() = 0;
virtual void sendStatsPacket();
signals:
void finished();
protected:
bool readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr);
void commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats = true);
bool _isFinished;
QThread* _datagramProcessingThread;
QTimer* _domainServerTimer = nullptr;
QTimer* _statsTimer = nullptr;

View file

@ -25,6 +25,14 @@ std::unique_ptr<Packet> Packet::create(PacketType::Value type, qint64 size) {
return std::unique_ptr<Packet>(new Packet(type, size));
}
std::unique_ptr<Packet> Packet::fromReceivedPacket(std::unique_ptr<char> data, qint64 size, const HifiSockAddr& senderSockAddr) {
// Fail with invalid size
Q_ASSERT(size >= 0);
// allocate memory
return std::unique_ptr<Packet>(new Packet(std::move(data), size, senderSockAddr));
}
std::unique_ptr<Packet> Packet::createCopy(const Packet& other) {
return std::unique_ptr<Packet>(new Packet(other));
}
@ -39,6 +47,7 @@ qint64 Packet::localHeaderSize() const {
Packet::Packet(PacketType::Value type, qint64 size) :
_type(type),
_version(0),
_packetSize(localHeaderSize(_type) + size),
_packet(new char(_packetSize)),
_payloadStart(_packet.get() + localHeaderSize(_type)),
@ -62,6 +71,18 @@ Packet::Packet(PacketType::Value type, qint64 size) :
}
}
Packet::Packet(std::unique_ptr<char> data, qint64 size, const HifiSockAddr& senderSockAddr) :
_packetSize(size),
_packet(std::move(data)),
_senderSockAddr(senderSockAddr)
{
_type = readType();
_version = readVersion();
_capacity = _packetSize - localHeaderSize(_type);
_sizeUsed = _capacity;
_payloadStart = _packet.get() + (_packetSize - _capacity);
}
Packet::Packet(const Packet& other) {
*this = other;
}

View file

@ -16,6 +16,7 @@
#include <QIODevice>
#include "HifiSockAddr.h"
#include "PacketHeaders.h"
class Packet : public QIODevice {
@ -24,6 +25,8 @@ public:
using SequenceNumber = uint16_t;
static std::unique_ptr<Packet> create(PacketType::Value type, qint64 size = -1);
static std::unique_ptr<Packet> fromReceivedPacket(std::unique_ptr<char> data, qint64 size, const HifiSockAddr& senderSockAddr);
// Provided for convenience, try to limit use
static std::unique_ptr<Packet> createCopy(const Packet& other);
@ -43,14 +46,16 @@ public:
PacketType::Value getType() const { return _type; }
void setType(PacketType::Value type);
PacketVersion getVersion() const { return _version; }
qint64 getSizeWithHeader() const { return localHeaderSize() + getSizeUsed(); }
qint64 getSizeUsed() const { return _sizeUsed; }
void setSizeUsed(qint64 sizeUsed) { _sizeUsed = sizeUsed; }
// Header readers
PacketType::Value readType() const;
PacketVersion readVersion() const;
HifiSockAddr& getSenderSockAddr() { return _senderSockAddr; }
const HifiSockAddr& getSenderSockAddr() const { return _senderSockAddr; }
SequenceNumber readSequenceNumber() const;
bool readIsControlPacket() const;
@ -60,16 +65,22 @@ public:
virtual bool reset() { setSizeUsed(0); return QIODevice::reset(); }
virtual qint64 size() const { return _capacity; }
template<typename T> qint64 peekPrimitive(T* data);
template<typename T> qint64 readPrimitive(T* data);
template<typename T> qint64 writePrimitive(const T& data);
protected:
Packet(PacketType::Value type, int64_t size);
Packet(std::unique_ptr<char> data, qint64 size, const HifiSockAddr& senderSockAddr);
Packet(const Packet& other);
Packet& operator=(const Packet& other);
Packet(Packet&& other);
Packet& operator=(Packet&& other);
// Header readers
PacketType::Value readType() const;
PacketVersion readVersion() const;
// QIODevice virtual functions
virtual qint64 writeData(const char* data, qint64 maxSize);
virtual qint64 readData(char* data, qint64 maxSize);
@ -79,6 +90,7 @@ protected:
void writeSequenceNumber(SequenceNumber seqNum);
PacketType::Value _type; // Packet type
PacketVersion _version; // Packet version
qint64 _packetSize = 0; // Total size of the allocated memory
std::unique_ptr<char> _packet; // Allocated memory
@ -87,9 +99,15 @@ protected:
qint64 _capacity = 0; // Total capacity of the payload
qint64 _sizeUsed = 0; // How much of the payload is actually used
HifiSockAddr _senderSockAddr; // sender address for packet (only used on receiving end)
};
template<typename T> qint64 Packet::peekPrimitive(T* data) {
return QIODevice::peek(reinterpret_cast<char*>(data), sizeof(T));
}
template<typename T> qint64 Packet::readPrimitive(T* data) {
return QIODevice::read(reinterpret_cast<char*>(data), sizeof(T));
}

View file

@ -18,21 +18,23 @@
using namespace PacketType;
const QSet<PacketType::Value> NON_VERIFIED_PACKETS = QSet<PacketType::Value>()
<< DomainServerRequireDTLS << DomainConnectRequest
<< DomainList << DomainListRequest << DomainConnectionDenied
<< CreateAssignment << RequestAssignment << StunResponse
<< NodeJsonStats << EntityQuery
<< OctreeDataNack << EntityEditNack
<< DomainListRequest
<< Ping
<< PingReply << StopNode
<< DomainServerPathQuery << DomainServerPathResponse
<< DomainServerAddedNode;
<< PingReply << StopNode;
const QSet<PacketType::Value> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType::Value>() << AvatarData;
const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>()
<< DomainServerRequireDTLS << DomainConnectRequest
<< DomainList << DomainConnectionDenied
<< DomainServerPathQuery << DomainServerPathResponse
<< DomainServerAddedNode
<< ICEServerPeerInformation << ICEServerQuery << ICEServerHeartbeat
<< ICEPing << ICEPingReply << DomainConnectRequest;
<< ICEPing << ICEPingReply
<< AssignmentClientStatus;
int arithmeticCodingValueFromBuffer(const char* checkValue) {
if (((uchar) *checkValue) < 255) {
@ -208,15 +210,6 @@ int sequenceNumberOffsetForPacketType(PacketType::Value packetType) {
return numBytesForPacketHeaderGivenPacketType(packetType) - sizeof(PacketSequenceNumber);
}
QByteArray hashFromPacketHeader(const QByteArray& packet) {
return packet.mid(hashOffsetForPacketType(packetTypeForPacket(packet)), NUM_BYTES_MD5_HASH);
}
QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID) {
return QCryptographicHash::hash(packet.mid(numBytesForPacketHeader(packet)) + connectionUUID.toRfc4122(),
QCryptographicHash::Md5);
}
PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketType::Value packetType) {
if (packetType == PacketType::Unknown) {
packetType = packetTypeForPacket(packet);

View file

@ -57,7 +57,7 @@ namespace PacketType {
OctreeStats,
Jurisdiction,
JurisdictionRequest,
UNUSED_6,
AssignmentClientStatus,
UNUSED_7, // 30
UNUSED_8,
UNUSED_9,
@ -122,9 +122,6 @@ QUuid uuidFromPacketHeader(const QByteArray& packet);
int hashOffsetForPacketType(PacketType::Value packetType);
int sequenceNumberOffsetForPacketType(PacketType::Value packetType);
QByteArray hashFromPacketHeader(const QByteArray& packet);
QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID);
// NOTE: The following four methods accept a PacketType::Value which defaults to PacketType::Unknown.
// If the caller has already looked at the packet type and can provide it then the methods below won't have to look it up.

View file

@ -56,12 +56,11 @@ bool JurisdictionListener::queueJurisdictionRequest() {
return isStillRunning();
}
void JurisdictionListener::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
if (packetTypeForPacket(packet) == PacketType::Jurisdiction && sendingNode) {
QUuid nodeUUID = sendingNode->getUUID();
void JurisdictionListener::processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
if (packet->getType() == PacketType::Jurisdiction) {
JurisdictionMap map;
map.unpackFromMessage(reinterpret_cast<const unsigned char*>(packet.data()), packet.size());
_jurisdictions[nodeUUID] = map;
map.unpackFromPacket(*packet);
_jurisdictions[packet->getSourceID()] = map;
}
}

View file

@ -47,7 +47,7 @@ public slots:
protected:
/// Callback for processing of received packets. Will process any queued PacketType::_JURISDICTION and update the
/// jurisdiction map member variable
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
private:
NodeToJurisdictionMap _jurisdictions;

View file

@ -280,7 +280,7 @@ std::unique_ptr<NLPacket> JurisdictionMap::packEmptyJurisdictionIntoMessage(Node
return std::move(packet); // includes header!
}
std::unique_ptr<NLPacket> JurisdictionMap::packIntoMessage() {
std::unique_ptr<NLPacket> JurisdictionMap::packIntoPacket() {
auto packet = NLPacket::create(PacketType::Jurisdiction);
// Pack the Node Type in first byte
@ -315,42 +315,28 @@ std::unique_ptr<NLPacket> JurisdictionMap::packIntoMessage() {
return std::move(packet);
}
int JurisdictionMap::unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes) {
int JurisdictionMap::unpackFromPacket(NLPacket& packet) {
clear();
const unsigned char* startPosition = sourceBuffer;
// increment to push past the packet header
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(sourceBuffer));
sourceBuffer += numBytesPacketHeader;
int remainingBytes = availableBytes - numBytesPacketHeader;
// read the root jurisdiction
int bytes = 0;
memcpy(&bytes, sourceBuffer, sizeof(bytes));
sourceBuffer += sizeof(bytes);
remainingBytes -= sizeof(bytes);
packet.readPrimitive(&bytes);
if (bytes > 0 && bytes <= remainingBytes) {
if (bytes > 0 && bytes <= packet.bytesAvailable()) {
_rootOctalCode = new unsigned char[bytes];
memcpy(_rootOctalCode, sourceBuffer, bytes);
sourceBuffer += bytes;
remainingBytes -= bytes;
packet.read(reinterpret_cast<char*>(_rootOctalCode), bytes);
// if and only if there's a root jurisdiction, also include the end nodes
int endNodeCount = 0;
memcpy(&endNodeCount, sourceBuffer, sizeof(endNodeCount));
sourceBuffer += sizeof(endNodeCount);
for (int i=0; i < endNodeCount; i++) {
packet.readPrimitive(&endNodeCount);
for (int i = 0; i < endNodeCount; i++) {
int bytes = 0;
memcpy(&bytes, sourceBuffer, sizeof(bytes));
sourceBuffer += sizeof(bytes);
remainingBytes -= sizeof(bytes);
packet.readPrimitive(&bytes);
if (bytes <= remainingBytes) {
if (bytes <= packet.bytesAvailable()) {
unsigned char* endNodeCode = new unsigned char[bytes];
memcpy(endNodeCode, sourceBuffer, bytes);
sourceBuffer += bytes;
remainingBytes -= bytes;
packet.read(reinterpret_cast<char*>(endNodeCode), bytes);
// if the endNodeCode was 0 length then don't add it
if (bytes > 0) {
@ -360,5 +346,5 @@ int JurisdictionMap::unpackFromMessage(const unsigned char* sourceBuffer, int av
}
}
return sourceBuffer - startPosition; // includes header!
return packet.pos(); // excludes header
}

View file

@ -61,8 +61,8 @@ public:
void copyContents(unsigned char* rootCodeIn, const std::vector<unsigned char*>& endNodesIn);
int unpackFromMessage(const unsigned char* sourceBuffer, int availableBytes);
std::unique_ptr<NLPacket> packIntoMessage();
int unpackFromPacket(NLPacket& packet);
std::unique_ptr<NLPacket> packIntoPacket();
/// Available to pack an empty or unknown jurisdiction into a network packet, used when no JurisdictionMap is available
static std::unique_ptr<NLPacket> packEmptyJurisdictionIntoMessage(NodeType_t type);

View file

@ -28,13 +28,11 @@ JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NodeType_t type) :
JurisdictionSender::~JurisdictionSender() {
}
void JurisdictionSender::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) {
if (packetTypeForPacket(packet) == PacketType::JurisdictionRequest) {
if (sendingNode) {
lockRequestingNodes();
_nodesRequestingJurisdictions.push(sendingNode->getUUID());
unlockRequestingNodes();
}
void JurisdictionSender::processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
if (packet->getType() == PacketType::JurisdictionRequest) {
lockRequestingNodes();
_nodesRequestingJurisdictions.push(sendingNode->getUUID());
unlockRequestingNodes();
}
}
@ -43,7 +41,7 @@ bool JurisdictionSender::process() {
// call our ReceivedPacketProcessor base class process so we'll get any pending packets
if (continueProcessing && (continueProcessing = ReceivedPacketProcessor::process())) {
auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoMessage()
auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket()
: JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType());
int nodeCount = 0;

View file

@ -38,7 +38,7 @@ public:
void setNodeType(NodeType_t type) { _nodeType = type; }
protected:
virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet);
virtual void processPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
/// Locks all the resources of the thread.
void lockRequestingNodes() { _requestingNodeMutex.lock(); }

View file

@ -229,12 +229,12 @@ public:
// own definition. Implement these to allow your octree based server to support editing
virtual bool getWantSVOfileVersions() const { return false; }
virtual PacketType::Value expectedDataPacketType() const { return PacketType::Unknown; }
virtual bool canProcessVersion(PacketVersion thisVersion) const {
virtual bool canProcessVersion(PacketVersion thisVersion) const {
return thisVersion == versionForPacketType(expectedDataPacketType()); }
virtual PacketVersion expectedVersion() const { return versionForPacketType(expectedDataPacketType()); }
virtual bool handlesEditPacketType(PacketType::Value packetType) const { return false; }
virtual int processEditPacketData(PacketType::Value packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; }
virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
const SharedNodePointer& sourceNode) { return 0; }
virtual bool recurseChildrenWithData() const { return true; }
virtual bool rootElementHasData() const { return false; }
@ -304,16 +304,16 @@ public:
} lockType;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElement*& node, float& distance, BoxFace& face,
OctreeElement*& node, float& distance, BoxFace& face,
void** intersectedObject = NULL,
Octree::lockType lockType = Octree::TryLock,
bool* accurateResult = NULL,
Octree::lockType lockType = Octree::TryLock,
bool* accurateResult = NULL,
bool precisionPicking = false);
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject = NULL,
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject = NULL,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration,
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
/// \param cube query cube in world-frame (meters)
@ -323,7 +323,7 @@ public:
/// \param point query point in world-frame (meters)
/// \param lockType how to lock the tree (Lock, TryLock, NoLock)
/// \param[out] accurateResult pointer to output result, will be set "true" or "false" if non-null
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
// Note: this assumes the fileFormat is the HIO individual voxels code files
@ -411,7 +411,7 @@ protected:
QReadWriteLock _lock;
bool _isViewing;
bool _isViewing;
bool _isServer;
};

View file

@ -225,10 +225,10 @@ void OctreeHeadlessViewer::queryOctree() {
}
int OctreeHeadlessViewer::parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sourceNode) {
int OctreeHeadlessViewer::parseOctreeStats(QSharedPointer<NLPacket> packet, SharedNodePointer sourceNode) {
OctreeSceneStats temp;
int statsMessageLength = temp.unpackFromMessage(reinterpret_cast<const unsigned char*>(packet.data()), packet.size());
int statsMessageLength = temp.unpackFromPacket(*packet);
// TODO: actually do something with these stats, like expose them to JS...

View file

@ -37,7 +37,7 @@ public:
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
static int parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sourceNode);
static int parseOctreeStats(QSharedPointer<NLPacket> packet, SharedNodePointer sourceNode);
static void trackIncomingOctreePacket(const QByteArray& packet, const SharedNodePointer& sendingNode, bool wasStatsPacket);
public slots:

View file

@ -64,13 +64,10 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
}
// called on the other nodes - assigns it to my views of the others
int OctreeQuery::parseData(const QByteArray& packet) {
// increment to push past the packet header
int numBytesPacketHeader = numBytesForPacketHeader(packet);
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(packet.data());
const unsigned char* sourceBuffer = startPosition + numBytesPacketHeader;
int OctreeQuery::parseData(NLPacket& packet) {
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(packet.getPayload());
const unsigned char* sourceBuffer = startPosition;
// camera details
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));

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