Merge branch 'master' of github.com:highfidelity/hifi into stack-manager

This commit is contained in:
Leonardo Murillo 2015-11-20 17:43:25 -06:00
commit 5d2700309d
22 changed files with 208 additions and 116 deletions

View file

@ -22,6 +22,7 @@
#include <NodeList.h> #include <NodeList.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include <ResourceCache.h> #include <ResourceCache.h>
#include <ScriptCache.h>
#include <SoundCache.h> #include <SoundCache.h>
#include <UUID.h> #include <UUID.h>
@ -116,6 +117,11 @@ void Agent::handleAudioPacket(QSharedPointer<NLPacket> packet) {
const QString AGENT_LOGGING_NAME = "agent"; const QString AGENT_LOGGING_NAME = "agent";
void Agent::run() { void Agent::run() {
// make sure we request our script once the agent connects to the domain
auto nodeList = DependencyManager::get<NodeList>();
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
// Setup MessagesClient // Setup MessagesClient
@ -125,72 +131,99 @@ void Agent::run() {
messagesClient->moveToThread(messagesThread); messagesClient->moveToThread(messagesThread);
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
messagesThread->start(); messagesThread->start();
nodeList->addSetOfNodeTypesToNodeInterestSet({
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer
});
}
void Agent::requestScript() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() disconnect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
<< NodeType::AudioMixer
<< NodeType::AvatarMixer
<< NodeType::EntityServer
<< NodeType::MessagesMixer
);
// figure out the URL for the script for this agent assignment // figure out the URL for the script for this agent assignment
QUrl scriptURL; QUrl scriptURL;
if (_payload.isEmpty()) { if (_payload.isEmpty()) {
scriptURL = QUrl(QString("http://%1:%2/assignment/%3") scriptURL = QUrl(QString("http://%1:%2/assignment/%3/")
.arg(DependencyManager::get<NodeList>()->getDomainHandler().getIP().toString()) .arg(nodeList->getDomainHandler().getIP().toString())
.arg(DOMAIN_SERVER_HTTP_PORT) .arg(DOMAIN_SERVER_HTTP_PORT)
.arg(uuidStringWithoutCurlyBraces(_uuid))); .arg(uuidStringWithoutCurlyBraces(nodeList->getSessionUUID())));
} else { } else {
scriptURL = QUrl(_payload); scriptURL = QUrl(_payload);
} }
// setup a network access manager and
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(scriptURL);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
QNetworkDiskCache* cache = new QNetworkDiskCache(); QNetworkDiskCache* cache = new QNetworkDiskCache();
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
networkAccessManager.setCache(cache); networkAccessManager.setCache(cache);
QNetworkRequest networkRequest = QNetworkRequest(scriptURL);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
// setup a timeout for script request
static const int SCRIPT_TIMEOUT_MS = 10000;
_scriptRequestTimeout = new QTimer(this);
connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished);
_scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS);
qDebug() << "Downloading script at" << scriptURL.toString(); qDebug() << "Downloading script at" << scriptURL.toString();
QNetworkReply* reply = networkAccessManager.get(networkRequest);
connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished);
}
QEventLoop loop; void Agent::scriptRequestFinished() {
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); auto reply = qobject_cast<QNetworkReply*>(sender());
loop.exec(); _scriptRequestTimeout->stop();
if (reply && reply->error() == QNetworkReply::NoError) {
_scriptContents = reply->readAll();
qDebug() << "Downloaded script:" << _scriptContents;
// we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished
// to return before calling executeScript
QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection);
} else {
if (reply) {
qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment.";
qDebug() << "QNetworkReply error was" << reply->errorString();
} else {
qDebug() << "Failed to download script - request timed out. Bailing on assignment.";
}
setFinished(true);
}
reply->deleteLater();
}
QString scriptContents(reply->readAll()); void Agent::executeScript() {
delete reply; _scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(_scriptContents, _payload));
qDebug() << "Downloaded script:" << scriptContents;
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(scriptContents, _payload));
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
// setup an Avatar for the script to use // setup an Avatar for the script to use
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>(); auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection);
scriptedAvatar->setForceFaceTrackerConnected(true); scriptedAvatar->setForceFaceTrackerConnected(true);
// call model URL setters with empty URLs so our avatar, if user, will have the default models // call model URL setters with empty URLs so our avatar, if user, will have the default models
scriptedAvatar->setFaceModelURL(QUrl()); scriptedAvatar->setFaceModelURL(QUrl());
scriptedAvatar->setSkeletonModelURL(QUrl()); scriptedAvatar->setSkeletonModelURL(QUrl());
// give this AvatarData object to the script engine // give this AvatarData object to the script engine
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data()); _scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
using namespace recording; using namespace recording;
static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME); static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME);
// FIXME how to deal with driving multiple avatars locally? // FIXME how to deal with driving multiple avatars locally?
Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) { Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) {
AvatarData::fromFrame(frame->data, *scriptedAvatar); AvatarData::fromFrame(frame->data, *scriptedAvatar);
}); });
using namespace recording; using namespace recording;
static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME); static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME);
Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) {
@ -201,32 +234,30 @@ void Agent::run() {
audioTransform.setRotation(scriptedAvatar->getOrientation()); audioTransform.setRotation(scriptedAvatar->getOrientation());
AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber, audioTransform, PacketType::MicrophoneAudioNoEcho); AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber, audioTransform, PacketType::MicrophoneAudioNoEcho);
}); });
auto avatarHashMap = DependencyManager::set<AvatarHashMap>(); auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
_scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data());
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver(); auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket");
packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar");
packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket");
packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket"); packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket");
// register ourselves to the script engine // register ourselves to the script engine
_scriptEngine->registerGlobalObject("Agent", this); _scriptEngine->registerGlobalObject("Agent", this);
// FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why
// viewers would need this called. // viewers would need this called.
//_scriptEngine->init(); // must be done before we set up the viewers //_scriptEngine->init(); // must be done before we set up the viewers
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data()); _scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor); QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); _scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
// we need to make sure that init has been called for our EntityScriptingInterface // we need to make sure that init has been called for our EntityScriptingInterface
@ -237,15 +268,15 @@ void Agent::run() {
_entityViewer.init(); _entityViewer.init();
entityScriptingInterface->setEntityTree(_entityViewer.getTree()); entityScriptingInterface->setEntityTree(_entityViewer.getTree());
// wire up our additional agent related processing to the update signal // wire up our additional agent related processing to the update signal
QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio); QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
_scriptEngine->run(); _scriptEngine->run();
Frame::clearFrameHandler(AUDIO_FRAME_TYPE); Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
Frame::clearFrameHandler(AVATAR_FRAME_TYPE); Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
setFinished(true); setFinished(true);
} }

View file

@ -55,6 +55,10 @@ public slots:
void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); } void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); }
private slots: private slots:
void requestScript();
void scriptRequestFinished();
void executeScript();
void handleAudioPacket(QSharedPointer<NLPacket> packet); void handleAudioPacket(QSharedPointer<NLPacket> packet);
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode); void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
@ -73,6 +77,8 @@ private:
void sendAvatarIdentityPacket(); void sendAvatarIdentityPacket();
void sendAvatarBillboardPacket(); void sendAvatarBillboardPacket();
QString _scriptContents;
QTimer* _scriptRequestTimeout { nullptr };
bool _isListeningToAudioStream = false; bool _isListeningToAudioStream = false;
Sound* _avatarSound = nullptr; Sound* _avatarSound = nullptr;
int _numAvatarSoundSentBytes = 0; int _numAvatarSoundSentBytes = 0;

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <memory>
#include <signal.h> #include <signal.h>
#include <AddressManager.h> #include <AddressManager.h>
@ -227,8 +228,9 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<NLPacket> p
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode
(senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false); (senderID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false, false);
childData = new AssignmentClientChildData(Assignment::Type::AllTypes); auto childData = std::unique_ptr<AssignmentClientChildData>
matchingNode->setLinkedData(childData); { new AssignmentClientChildData(Assignment::Type::AllTypes) };
matchingNode->setLinkedData(std::move(childData));
} else { } else {
// tell unknown assignment-client child to exit. // tell unknown assignment-client child to exit.
qDebug() << "Asking unknown child at" << senderSockAddr << "to exit."; qDebug() << "Asking unknown child at" << senderSockAddr << "to exit.";

View file

@ -14,6 +14,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <math.h> #include <math.h>
#include <memory>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -661,7 +662,7 @@ void AudioMixer::domainSettingsRequestComplete() {
nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [](Node* node) { nodeList->linkedDataCreateCallback = [](Node* node) {
node->setLinkedData(new AudioMixerClientData()); node->setLinkedData(std::unique_ptr<AudioMixerClientData> { new AudioMixerClientData });
}; };
DomainHandler& domainHandler = nodeList->getDomainHandler(); DomainHandler& domainHandler = nodeList->getDomainHandler();

View file

@ -11,6 +11,7 @@
#include <cfloat> #include <cfloat>
#include <random> #include <random>
#include <memory>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
@ -536,7 +537,7 @@ void AvatarMixer::domainSettingsRequestComplete() {
nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [] (Node* node) { nodeList->linkedDataCreateCallback = [] (Node* node) {
node->setLinkedData(new AvatarMixerClientData()); node->setLinkedData(std::unique_ptr<AvatarMixerClientData> { new AvatarMixerClientData });
}; };
// parse the settings to pull out the values we need // parse the settings to pull out the values we need

View file

@ -46,8 +46,8 @@ void EntityServer::handleEntityPacket(QSharedPointer<NLPacket> packet, SharedNod
} }
} }
OctreeQueryNode* EntityServer::createOctreeQueryNode() { std::unique_ptr<OctreeQueryNode> EntityServer::createOctreeQueryNode() {
return new EntityNodeData(); return std::unique_ptr<OctreeQueryNode> { new EntityNodeData() };
} }
OctreePointer EntityServer::createTree() { OctreePointer EntityServer::createTree() {

View file

@ -14,6 +14,8 @@
#include "../octree/OctreeServer.h" #include "../octree/OctreeServer.h"
#include <memory>
#include "EntityItem.h" #include "EntityItem.h"
#include "EntityServerConsts.h" #include "EntityServerConsts.h"
#include "EntityTree.h" #include "EntityTree.h"
@ -26,7 +28,7 @@ public:
~EntityServer(); ~EntityServer();
// Subclasses must implement these methods // Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode() override ; virtual std::unique_ptr<OctreeQueryNode> createOctreeQueryNode() override ;
virtual char getMyNodeType() const override { return NodeType::EntityServer; } virtual char getMyNodeType() const override { return NodeType::EntityServer; }
virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; }
virtual const char* getMyServerName() const override { return MODEL_SERVER_NAME; } virtual const char* getMyServerName() const override { return MODEL_SERVER_NAME; }

View file

@ -1113,9 +1113,9 @@ void OctreeServer::domainSettingsRequestComplete() {
#endif #endif
nodeList->linkedDataCreateCallback = [] (Node* node) { nodeList->linkedDataCreateCallback = [] (Node* node) {
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); auto queryNodeData = _instance->createOctreeQueryNode();
newQueryNodeData->init(); queryNodeData->init();
node->setLinkedData(newQueryNodeData); node->setLinkedData(std::move(queryNodeData));
}; };
srand((unsigned)time(0)); srand((unsigned)time(0));

View file

@ -12,6 +12,8 @@
#ifndef hifi_OctreeServer_h #ifndef hifi_OctreeServer_h
#define hifi_OctreeServer_h #define hifi_OctreeServer_h
#include <memory>
#include <QStringList> #include <QStringList>
#include <QDateTime> #include <QDateTime>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
@ -64,7 +66,7 @@ public:
QByteArray getPersistFileContents() const { return (_persistThread) ? _persistThread->getPersistFileContents() : QByteArray(); } QByteArray getPersistFileContents() const { return (_persistThread) ? _persistThread->getPersistFileContents() : QByteArray(); }
// Subclasses must implement these methods // Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode() = 0; virtual std::unique_ptr<OctreeQueryNode> createOctreeQueryNode() = 0;
virtual char getMyNodeType() const = 0; virtual char getMyNodeType() const = 0;
virtual PacketType getMyQueryMessageType() const = 0; virtual PacketType getMyQueryMessageType() const = 0;
virtual const char* getMyServerName() const = 0; virtual const char* getMyServerName() const = 0;

View file

@ -11,6 +11,8 @@
#include "DomainServer.h" #include "DomainServer.h"
#include <memory>
#include <QDir> #include <QDir>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@ -1097,29 +1099,37 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
if (connection->requestOperation() == QNetworkAccessManager::GetOperation if (connection->requestOperation() == QNetworkAccessManager::GetOperation
&& assignmentRegex.indexIn(url.path()) != -1) { && assignmentRegex.indexIn(url.path()) != -1) {
QUuid matchingUUID = QUuid(assignmentRegex.cap(1)); QUuid nodeUUID = QUuid(assignmentRegex.cap(1));
SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID); auto matchingNode = nodeList->nodeWithUUID(nodeUUID);
if (!matchingAssignment) {
// check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment // don't handle if we don't have a matching node
QUuid assignmentUUID = _gatekeeper.assignmentUUIDForPendingAssignment(matchingUUID); if (!matchingNode) {
if (!assignmentUUID.isNull()) { return false;
matchingAssignment = _allAssignments.value(assignmentUUID);
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
// via correct URL for the script so the client can download
QUrl scriptURL = url;
scriptURL.setPath(URI_ASSIGNMENT + "/scripts/"
+ uuidStringWithoutCurlyBraces(assignmentUUID));
// have the HTTPManager serve the appropriate script file
return _httpManager.handleHTTPRequest(connection, scriptURL, true);
}
}
} }
auto nodeData = dynamic_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
// don't handle if we don't have node data for this node
if (!nodeData) {
return false;
}
SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID());
// check if we have an assignment that matches this temp UUID, and it is a scripted assignment
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
// via correct URL for the script so the client can download
QUrl scriptURL = url;
scriptURL.setPath(URI_ASSIGNMENT + "/scripts/"
+ uuidStringWithoutCurlyBraces(matchingAssignment->getUUID()));
// have the HTTPManager serve the appropriate script file
return _httpManager.handleHTTPRequest(connection, scriptURL, true);
}
// request not handled // request not handled
return false; return false;
} }
@ -1640,7 +1650,7 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer&
void DomainServer::nodeAdded(SharedNodePointer node) { void DomainServer::nodeAdded(SharedNodePointer node) {
// we don't use updateNodeWithData, so add the DomainServerNodeData to the node here // we don't use updateNodeWithData, so add the DomainServerNodeData to the node here
node->setLinkedData(new DomainServerNodeData()); node->setLinkedData(std::unique_ptr<DomainServerNodeData> { new DomainServerNodeData() });
} }
void DomainServer::nodeKilled(SharedNodePointer node) { void DomainServer::nodeKilled(SharedNodePointer node) {

View file

@ -19,10 +19,11 @@ var MAX_LINE_LENGTH = 40; // This must be 2 or greater;
var DEFAULT_STROKE_WIDTH = 0.1; var DEFAULT_STROKE_WIDTH = 0.1;
var DEFAULT_LIFETIME = 20; var DEFAULT_LIFETIME = 20;
var DEFAULT_COLOR = { red: 255, green: 255, blue: 255 }; var DEFAULT_COLOR = { red: 255, green: 255, blue: 255 };
var PolyLine = function(position, color, lifetime) { var PolyLine = function(position, color, lifetime, texture) {
this.position = position; this.position = position;
this.color = color; this.color = color;
this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime; this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime;
this.texture = texture ? texture : "";
this.points = [ this.points = [
]; ];
this.strokeWidths = [ this.strokeWidths = [
@ -37,7 +38,8 @@ var PolyLine = function(position, color, lifetime) {
strokeWidths: this.strokeWidths, strokeWidths: this.strokeWidths,
dimensions: LINE_DIMENSIONS, dimensions: LINE_DIMENSIONS,
color: color, color: color,
lifetime: lifetime lifetime: lifetime,
textures: this.texture
}); });
}; };
@ -98,26 +100,29 @@ PolyLine.prototype.destroy = function() {
// InfiniteLine // InfiniteLine
InfiniteLine = function(position, color, lifetime) { InfiniteLine = function(position, color, lifetime, textureBegin, textureMiddle) {
this.position = position; this.position = position;
this.color = color; this.color = color;
this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime; this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime;
this.lines = []; this.lines = [];
this.size = 0; this.size = 0;
this.textureBegin = textureBegin ? textureBegin : "";
this.textureMiddle = textureMiddle ? textureMiddle : "";
}; };
InfiniteLine.prototype.enqueuePoint = function(position, strokeWidth) { InfiniteLine.prototype.enqueuePoint = function(position, strokeWidth) {
var currentLine; var currentLine;
if (this.lines.length == 0) { if (this.lines.length == 0) {
currentLine = new PolyLine(position, this.color, this.lifetime); currentLine = new PolyLine(position, this.color, this.lifetime, this.textureBegin);
this.lines.push(currentLine); this.lines.push(currentLine);
} else { } else {
currentLine = this.lines[this.lines.length - 1]; currentLine = this.lines[this.lines.length - 1];
} }
if (currentLine.isFull()) { if (currentLine.isFull()) {
var newLine = new PolyLine(currentLine.getLastPoint(), this.color, this.lifetime); var newLine = new PolyLine(currentLine.getLastPoint(), this.color, this.lifetime, this.textureMiddle);
newLine.enqueuePoint(currentLine.getLastPoint(), strokeWidth); newLine.enqueuePoint(currentLine.getLastPoint(), strokeWidth);
this.lines.push(newLine); this.lines.push(newLine);
currentLine = newLine; currentLine = newLine;

View file

@ -801,6 +801,14 @@ void AvatarData::changeReferential(Referential* ref) {
_referential = ref; _referential = ref;
} }
void AvatarData::setRawJointData(QVector<JointData> data) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setRawJointData", Q_ARG(QVector<JointData>, data));
return;
}
_jointData = data;
}
void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) {
if (index == -1) { if (index == -1) {
return; return;
@ -1538,16 +1546,15 @@ void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) {
QVector<JointData> jointArray; QVector<JointData> jointArray;
QJsonArray jointArrayJson = root[JSON_AVATAR_JOINT_ARRAY].toArray(); QJsonArray jointArrayJson = root[JSON_AVATAR_JOINT_ARRAY].toArray();
jointArray.reserve(jointArrayJson.size()); jointArray.reserve(jointArrayJson.size());
int i = 0;
for (const auto& jointJson : jointArrayJson) { for (const auto& jointJson : jointArrayJson) {
jointArray.push_back(jointDataFromJsonValue(jointJson)); auto joint = jointDataFromJsonValue(jointJson);
jointArray.push_back(joint);
result.setJointData(i, joint.rotation, joint.translation);
result._jointData[i].rotationSet = true; // Have to do that to broadcast the avatar new pose
i++;
} }
result.setRawJointData(jointArray);
QVector<glm::quat> jointRotations;
jointRotations.reserve(jointArray.size());
for (const auto& joint : jointArray) {
jointRotations.push_back(joint.rotation);
}
result.setJointRotations(jointRotations);
} }
#if 0 #if 0

View file

@ -247,7 +247,7 @@ public:
Q_INVOKABLE char getHandState() const { return _handState; } Q_INVOKABLE char getHandState() const { return _handState; }
const QVector<JointData>& getRawJointData() const { return _jointData; } const QVector<JointData>& getRawJointData() const { return _jointData; }
void setRawJointData(QVector<JointData> data) { _jointData = data; } Q_INVOKABLE void setRawJointData(QVector<JointData> data);
Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation);
Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation);

View file

@ -36,7 +36,6 @@ PolyLineEntityItem(entityItemID, properties) {
gpu::PipelinePointer RenderablePolyLineEntityItem::_pipeline; gpu::PipelinePointer RenderablePolyLineEntityItem::_pipeline;
gpu::Stream::FormatPointer RenderablePolyLineEntityItem::_format; gpu::Stream::FormatPointer RenderablePolyLineEntityItem::_format;
gpu::TexturePointer RenderablePolyLineEntityItem::_texture;
int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT; int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT;
void RenderablePolyLineEntityItem::createPipeline() { void RenderablePolyLineEntityItem::createPipeline() {
@ -44,9 +43,6 @@ void RenderablePolyLineEntityItem::createPipeline() {
static const int COLOR_OFFSET = 24; static const int COLOR_OFFSET = 24;
static const int TEXTURE_OFFSET = 28; static const int TEXTURE_OFFSET = 28;
auto textureCache = DependencyManager::get<TextureCache>();
QString path = PathUtils::resourcesPath() + "images/paintStroke.png";
_texture = textureCache->getImageTexture(path);
_format.reset(new gpu::Stream::Format()); _format.reset(new gpu::Stream::Format());
_format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
_format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET); _format->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), NORMAL_OFFSET);
@ -132,6 +128,13 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
createPipeline(); createPipeline();
} }
if (!_texture || _texturesChangedFlag) {
auto textureCache = DependencyManager::get<TextureCache>();
QString path = _textures.isEmpty() ? PathUtils::resourcesPath() + "images/paintStroke.png" : _textures;
_texture = textureCache->getTexture(QUrl(path));
_texturesChangedFlag = false;
}
PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render");
Q_ASSERT(getType() == EntityTypes::PolyLine); Q_ASSERT(getType() == EntityTypes::PolyLine);
@ -147,7 +150,11 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
batch.setModelTransform(transform); batch.setModelTransform(transform);
batch.setPipeline(_pipeline); batch.setPipeline(_pipeline);
batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, _texture); if (_texture->isLoaded()) {
batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, _texture->getGPUTexture());
} else {
batch.setResourceTexture(PAINTSTROKE_GPU_SLOT, args->_whiteTexture);
}
batch.setInputFormat(_format); batch.setInputFormat(_format);
batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride);

View file

@ -12,10 +12,13 @@
#ifndef hifi_RenderablePolyLineEntityItem_h #ifndef hifi_RenderablePolyLineEntityItem_h
#define hifi_RenderablePolyLineEntityItem_h #define hifi_RenderablePolyLineEntityItem_h
#include <gpu/Batch.h> #include <gpu/Batch.h>
#include <GeometryCache.h>
#include <PolyLineEntityItem.h> #include <PolyLineEntityItem.h>
#include "RenderableEntityItem.h" #include "RenderableEntityItem.h"
#include <GeometryCache.h> #include <TextureCache.h>
#include <QReadWriteLock> #include <QReadWriteLock>
@ -29,9 +32,10 @@ public:
SIMPLE_RENDERABLE(); SIMPLE_RENDERABLE();
NetworkTexturePointer _texture;
static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipeline;
static gpu::Stream::FormatPointer _format; static gpu::Stream::FormatPointer _format;
static gpu::TexturePointer _texture;
static int32_t PAINTSTROKE_GPU_SLOT; static int32_t PAINTSTROKE_GPU_SLOT;
protected: protected:

View file

@ -432,6 +432,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures);
} }
// Sitting properties support // Sitting properties support
@ -1011,6 +1012,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints());
APPEND_ENTITY_PROPERTY(PROP_NORMALS, properties.getNormals()); APPEND_ENTITY_PROPERTY(PROP_NORMALS, properties.getNormals());
APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths()); APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths());
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures());
} }
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
@ -1287,6 +1289,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector<glm::vec3>, setLinePoints); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector<glm::vec3>, setLinePoints);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NORMALS, QVector<glm::vec3>, setNormals); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NORMALS, QVector<glm::vec3>, setNormals);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector<float>, setStrokeWidths); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector<float>, setStrokeWidths);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures);
} }
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);

View file

@ -37,7 +37,8 @@ _pointsChanged(true),
_points(QVector<glm::vec3>(0.0f)), _points(QVector<glm::vec3>(0.0f)),
_vertices(QVector<glm::vec3>(0.0f)), _vertices(QVector<glm::vec3>(0.0f)),
_normals(QVector<glm::vec3>(0.0f)), _normals(QVector<glm::vec3>(0.0f)),
_strokeWidths(QVector<float>(0.0f)) _strokeWidths(QVector<float>(0.0f)),
_textures("")
{ {
_type = EntityTypes::PolyLine; _type = EntityTypes::PolyLine;
_created = properties.getCreated(); _created = properties.getCreated();
@ -56,6 +57,7 @@ EntityItemProperties PolyLineEntityItem::getProperties(EntityPropertyFlags desir
COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals); COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeWidths, getStrokeWidths); COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeWidths, getStrokeWidths);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
properties._glowLevel = getGlowLevel(); properties._glowLevel = getGlowLevel();
properties._glowLevelChanged = false; properties._glowLevelChanged = false;
@ -72,6 +74,7 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(normals, setNormals); SET_ENTITY_PROPERTY_FROM_PROPERTIES(normals, setNormals);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeWidths, setStrokeWidths); SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeWidths, setStrokeWidths);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
if (somethingChanged) { if (somethingChanged) {
bool wantDebug = false; bool wantDebug = false;
@ -196,6 +199,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector<glm::vec3>, setLinePoints); READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector<glm::vec3>, setLinePoints);
READ_ENTITY_PROPERTY(PROP_NORMALS, QVector<glm::vec3>, setNormals); READ_ENTITY_PROPERTY(PROP_NORMALS, QVector<glm::vec3>, setNormals);
READ_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, QVector<float>, setStrokeWidths); READ_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, QVector<float>, setStrokeWidths);
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
return bytesRead; return bytesRead;
} }
@ -209,6 +213,7 @@ EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParam
requestedProperties += PROP_LINE_POINTS; requestedProperties += PROP_LINE_POINTS;
requestedProperties += PROP_NORMALS; requestedProperties += PROP_NORMALS;
requestedProperties += PROP_STROKE_WIDTHS; requestedProperties += PROP_STROKE_WIDTHS;
requestedProperties += PROP_TEXTURES;
return requestedProperties; return requestedProperties;
} }
@ -228,6 +233,7 @@ void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, Encode
APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints()); APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints());
APPEND_ENTITY_PROPERTY(PROP_NORMALS, getNormals()); APPEND_ENTITY_PROPERTY(PROP_NORMALS, getNormals());
APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, getStrokeWidths()); APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, getStrokeWidths());
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures());
} }
void PolyLineEntityItem::debugDump() const { void PolyLineEntityItem::debugDump() const {

View file

@ -67,7 +67,14 @@ class PolyLineEntityItem : public EntityItem {
bool setStrokeWidths(const QVector<float>& strokeWidths); bool setStrokeWidths(const QVector<float>& strokeWidths);
const QVector<float>& getStrokeWidths() const{ return _strokeWidths; } const QVector<float>& getStrokeWidths() const{ return _strokeWidths; }
const QString& getTextures() const { return _textures; }
void setTextures(const QString& textures) {
if (_textures != textures) {
_textures = textures;
_texturesChangedFlag = true;
}
}
virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; }
@ -90,6 +97,8 @@ class PolyLineEntityItem : public EntityItem {
QVector<glm::vec3> _vertices; QVector<glm::vec3> _vertices;
QVector<glm::vec3> _normals; QVector<glm::vec3> _normals;
QVector<float> _strokeWidths; QVector<float> _strokeWidths;
QString _textures;
bool _texturesChangedFlag { false };
mutable QReadWriteLock _quadReadWriteLock; mutable QReadWriteLock _quadReadWriteLock;
}; };

View file

@ -48,7 +48,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
NetworkPeer(uuid, publicSocket, localSocket, parent), NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type), _type(type),
_connectionSecret(connectionSecret), _connectionSecret(connectionSecret),
_linkedData(NULL),
_isAlive(true), _isAlive(true),
_pingMs(-1), // "Uninitialized" _pingMs(-1), // "Uninitialized"
_clockSkewUsec(0), _clockSkewUsec(0),
@ -61,10 +60,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
setType(_type); setType(_type);
} }
Node::~Node() {
delete _linkedData;
}
void Node::setType(char type) { void Node::setType(char type) {
_type = type; _type = type;

View file

@ -12,6 +12,7 @@
#ifndef hifi_Node_h #ifndef hifi_Node_h
#define hifi_Node_h #define hifi_Node_h
#include <memory>
#include <ostream> #include <ostream>
#include <stdint.h> #include <stdint.h>
@ -34,7 +35,6 @@ public:
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
bool canAdjustLocks, bool canRez, const QUuid& connectionSecret = QUuid(), bool canAdjustLocks, bool canRez, const QUuid& connectionSecret = QUuid(),
QObject* parent = 0); QObject* parent = 0);
~Node();
bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; }
bool operator!=(const Node& otherNode) const { return !(*this == otherNode); } bool operator!=(const Node& otherNode) const { return !(*this == otherNode); }
@ -45,8 +45,8 @@ public:
const QUuid& getConnectionSecret() const { return _connectionSecret; } const QUuid& getConnectionSecret() const { return _connectionSecret; }
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; } void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
NodeData* getLinkedData() const { return _linkedData; } NodeData* getLinkedData() const { return _linkedData.get(); }
void setLinkedData(NodeData* linkedData) { _linkedData = linkedData; } void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
bool isAlive() const { return _isAlive; } bool isAlive() const { return _isAlive; }
void setAlive(bool isAlive) { _isAlive = isAlive; } void setAlive(bool isAlive) { _isAlive = isAlive; }
@ -75,7 +75,7 @@ private:
NodeType_t _type; NodeType_t _type;
QUuid _connectionSecret; QUuid _connectionSecret;
NodeData* _linkedData; std::unique_ptr<NodeData> _linkedData;
bool _isAlive; bool _isAlive;
int _pingMs; int _pingMs;
int _clockSkewUsec; int _clockSkewUsec;

View file

@ -41,7 +41,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityAdd: case PacketType::EntityAdd:
case PacketType::EntityEdit: case PacketType::EntityEdit:
case PacketType::EntityData: case PacketType::EntityData:
return VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING; return VERSION_ENTITIES_POLYLINE_TEXTURE;
case PacketType::AvatarData: case PacketType::AvatarData:
case PacketType::BulkAvatarData: case PacketType::BulkAvatarData:
default: default:

View file

@ -159,5 +159,6 @@ const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46;
const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47;
const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48;
const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49;
const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50;
#endif // hifi_PacketHeaders_h #endif // hifi_PacketHeaders_h