mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 01:53:10 +02:00
merge from upstream
This commit is contained in:
commit
9c4fdfd653
197 changed files with 8515 additions and 1928 deletions
|
@ -42,8 +42,8 @@ const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
|
|||
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
||||
|
||||
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
||||
quint16 assignmentMonitorPort) :
|
||||
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 assignmentServerPort, quint16 assignmentMonitorPort) :
|
||||
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
|
||||
{
|
||||
LogUtils::init();
|
||||
|
@ -53,7 +53,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
auto addressManager = DependencyManager::set<AddressManager>();
|
||||
|
||||
// create a NodeList as an unassigned client, must be after addressManager
|
||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned);
|
||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
||||
|
||||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>();
|
||||
|
@ -95,6 +95,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
}
|
||||
|
||||
_assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true);
|
||||
_assignmentServerSocket.setObjectName("AssigmentServer");
|
||||
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
|
||||
|
||||
qDebug() << "Assignment server socket is" << _assignmentServerSocket;
|
||||
|
@ -119,6 +120,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
// did we get an assignment-client monitor port?
|
||||
if (assignmentMonitorPort > 0) {
|
||||
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
|
||||
_assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");
|
||||
|
||||
qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ class AssignmentClient : public QObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
quint16 listenPort,
|
||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
||||
quint16 assignmentMonitorPort);
|
||||
~AssignmentClient();
|
||||
|
|
|
@ -59,6 +59,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
|
||||
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");
|
||||
parser.addOption(poolOption);
|
||||
|
||||
const QCommandLineOption portOption(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION,
|
||||
"UDP port for this assignment client (or monitor)", "port");
|
||||
parser.addOption(portOption);
|
||||
|
||||
const QCommandLineOption walletDestinationOption(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION,
|
||||
"set wallet destination", "wallet-uuid");
|
||||
|
@ -158,12 +162,18 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
// check for an overriden assignment server port
|
||||
quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
|
||||
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
|
||||
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toUInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(assignmentServerPortOption)) {
|
||||
assignmentServerPort = parser.value(assignmentServerPortOption).toInt();
|
||||
}
|
||||
|
||||
// check for an overidden listen port
|
||||
quint16 listenPort = 0;
|
||||
if (argumentVariantMap.contains(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION)) {
|
||||
listenPort = argumentVariantMap.value(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION).toUInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(numChildsOption)) {
|
||||
if (minForks && minForks > numForks) {
|
||||
|
@ -185,12 +195,12 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
if (numForks || minForks || maxForks) {
|
||||
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
|
||||
requestAssignmentType, assignmentPool,
|
||||
walletUUID, assignmentServerHostname,
|
||||
listenPort, walletUUID, assignmentServerHostname,
|
||||
assignmentServerPort);
|
||||
monitor->setParent(this);
|
||||
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
|
||||
} else {
|
||||
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool,
|
||||
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool, listenPort,
|
||||
walletUUID, assignmentServerHostname,
|
||||
assignmentServerPort, monitorPort);
|
||||
client->setParent(this);
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
|
||||
const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t";
|
||||
const QString ASSIGNMENT_POOL_OPTION = "pool";
|
||||
const QString ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION = "p";
|
||||
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet";
|
||||
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a";
|
||||
const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "p";
|
||||
const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "server-port";
|
||||
const QString ASSIGNMENT_NUM_FORKS_OPTION = "n";
|
||||
const QString ASSIGNMENT_MIN_FORKS_OPTION = "min";
|
||||
const QString ASSIGNMENT_MAX_FORKS_OPTION = "max";
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION = "monitor-port";
|
||||
|
||||
|
||||
class AssignmentClientApp : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -28,7 +28,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
const unsigned int minAssignmentClientForks,
|
||||
const unsigned int maxAssignmentClientForks,
|
||||
Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 assignmentServerPort) :
|
||||
_numAssignmentClientForks(numAssignmentClientForks),
|
||||
_minAssignmentClientForks(minAssignmentClientForks),
|
||||
|
@ -50,7 +50,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
// create a NodeList so we can receive stats from children
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
auto addressManager = DependencyManager::set<AddressManager>();
|
||||
auto nodeList = DependencyManager::set<LimitedNodeList>();
|
||||
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
||||
|
|
|
@ -28,7 +28,7 @@ class AssignmentClientMonitor : public QObject {
|
|||
public:
|
||||
AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks,
|
||||
const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType,
|
||||
QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname,
|
||||
QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 assignmentServerPort);
|
||||
~AssignmentClientMonitor();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "audio/AudioMixer.h"
|
||||
#include "avatars/AvatarMixer.h"
|
||||
#include "entities/EntityServer.h"
|
||||
#include "assets/AssetServer.h"
|
||||
|
||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
|
||||
|
||||
|
@ -33,6 +34,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
|
|||
return new Agent(packet);
|
||||
case Assignment::EntityServerType:
|
||||
return new EntityServer(packet);
|
||||
case Assignment::AssetServerType:
|
||||
return new AssetServer(packet);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
167
assignment-client/src/assets/AssetServer.cpp
Normal file
167
assignment-client/src/assets/AssetServer.cpp
Normal file
|
@ -0,0 +1,167 @@
|
|||
//
|
||||
// AssetServer.cpp
|
||||
// assignment-client/src/assets
|
||||
//
|
||||
// Created by Ryan Huffman on 2015/07/21
|
||||
// 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 "AssetServer.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QCoreApplication>
|
||||
#include <QEventLoop>
|
||||
#include <QRunnable>
|
||||
#include <QString>
|
||||
|
||||
#include "NetworkLogging.h"
|
||||
#include "NodeType.h"
|
||||
#include "SendAssetTask.h"
|
||||
#include "UploadAssetTask.h"
|
||||
|
||||
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
|
||||
|
||||
AssetServer::AssetServer(NLPacket& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
_taskPool(this)
|
||||
{
|
||||
|
||||
// Most of the work will be I/O bound, reading from disk and constructing packet objects,
|
||||
// so the ideal is greater than the number of cores on the system.
|
||||
static const int TASK_POOL_THREAD_COUNT = 50;
|
||||
_taskPool.setMaxThreadCount(TASK_POOL_THREAD_COUNT);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet");
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo");
|
||||
packetReceiver.registerMessageListener(PacketType::AssetUpload, this, "handleAssetUpload");
|
||||
}
|
||||
|
||||
void AssetServer::run() {
|
||||
ThreadedAssignment::commonInit(ASSET_SERVER_LOGGING_TARGET_NAME, NodeType::AssetServer);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
_resourcesDirectory = QDir(QCoreApplication::applicationDirPath()).filePath("resources/assets");
|
||||
if (!_resourcesDirectory.exists()) {
|
||||
qDebug() << "Creating resources directory";
|
||||
_resourcesDirectory.mkpath(".");
|
||||
}
|
||||
qDebug() << "Serving files from: " << _resourcesDirectory.path();
|
||||
|
||||
// Scan for new files
|
||||
qDebug() << "Looking for new files in asset directory";
|
||||
auto files = _resourcesDirectory.entryInfoList(QDir::Files);
|
||||
QRegExp filenameRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}(\\..+)?$" };
|
||||
for (const auto& fileInfo : files) {
|
||||
auto filename = fileInfo.fileName();
|
||||
if (!filenameRegex.exactMatch(filename)) {
|
||||
qDebug() << "Found file: " << filename;
|
||||
if (!fileInfo.isReadable()) {
|
||||
qDebug() << "\tCan't open file for reading: " << filename;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read file
|
||||
QFile file { fileInfo.absoluteFilePath() };
|
||||
file.open(QFile::ReadOnly);
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
auto hash = hashData(data);
|
||||
auto hexHash = hash.toHex();
|
||||
|
||||
qDebug() << "\tMoving " << filename << " to " << hexHash;
|
||||
|
||||
file.rename(_resourcesDirectory.absoluteFilePath(hexHash) + "." + fileInfo.suffix());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
QByteArray assetHash;
|
||||
MessageID messageID;
|
||||
uint8_t extensionLength;
|
||||
|
||||
if (packet->getPayloadSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID) + sizeof(extensionLength))) {
|
||||
qDebug() << "ERROR bad file request";
|
||||
return;
|
||||
}
|
||||
|
||||
packet->readPrimitive(&messageID);
|
||||
assetHash = packet->readWithoutCopy(SHA256_HASH_LENGTH);
|
||||
packet->readPrimitive(&extensionLength);
|
||||
QByteArray extension = packet->read(extensionLength);
|
||||
|
||||
auto replyPacket = NLPacket::create(PacketType::AssetGetInfoReply);
|
||||
|
||||
QByteArray hexHash = assetHash.toHex();
|
||||
|
||||
replyPacket->writePrimitive(messageID);
|
||||
replyPacket->write(assetHash);
|
||||
|
||||
QString fileName = QString(hexHash) + "." + extension;
|
||||
QFileInfo fileInfo { _resourcesDirectory.filePath(fileName) };
|
||||
|
||||
if (fileInfo.exists() && fileInfo.isReadable()) {
|
||||
qDebug() << "Opening file: " << fileInfo.filePath();
|
||||
replyPacket->writePrimitive(AssetServerError::NoError);
|
||||
replyPacket->writePrimitive(fileInfo.size());
|
||||
} else {
|
||||
qDebug() << "Asset not found: " << QString(hexHash);
|
||||
replyPacket->writePrimitive(AssetServerError::AssetNotFound);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendPacket(std::move(replyPacket), *senderNode);
|
||||
}
|
||||
|
||||
void AssetServer::handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
|
||||
auto minSize = qint64(sizeof(MessageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + sizeof(DataOffset) + sizeof(DataOffset));
|
||||
|
||||
if (packet->getPayloadSize() < minSize) {
|
||||
qDebug() << "ERROR bad file request";
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue task
|
||||
auto task = new SendAssetTask(packet, senderNode, _resourcesDirectory);
|
||||
_taskPool.start(task);
|
||||
}
|
||||
|
||||
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
|
||||
if (senderNode->getCanRez()) {
|
||||
qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID());
|
||||
|
||||
auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory);
|
||||
_taskPool.start(task);
|
||||
} else {
|
||||
// this is a node the domain told us is not allowed to rez entities
|
||||
// for now this also means it isn't allowed to add assets
|
||||
// so return a packet with error that indicates that
|
||||
|
||||
auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError));
|
||||
|
||||
MessageID messageID;
|
||||
packetList->readPrimitive(&messageID);
|
||||
|
||||
// write the message ID and a permission denied error
|
||||
permissionErrorPacket->writePrimitive(messageID);
|
||||
permissionErrorPacket->writePrimitive(AssetServerError::PermissionDenied);
|
||||
|
||||
// send off the packet
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode);
|
||||
}
|
||||
}
|
||||
|
45
assignment-client/src/assets/AssetServer.h
Normal file
45
assignment-client/src/assets/AssetServer.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// AssetServer.h
|
||||
// assignment-client/src/assets
|
||||
//
|
||||
// Created by Ryan Huffman on 2015/07/21
|
||||
// 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_AssetServer_h
|
||||
#define hifi_AssetServer_h
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <ThreadedAssignment.h>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include "AssetUtils.h"
|
||||
|
||||
class AssetServer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssetServer(NLPacket& packet);
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
|
||||
private slots:
|
||||
void handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
|
||||
private:
|
||||
static void writeError(NLPacketList* packetList, AssetServerError error);
|
||||
QDir _resourcesDirectory;
|
||||
QThreadPool _taskPool;
|
||||
};
|
||||
|
||||
inline void writeError(NLPacketList* packetList, AssetServerError error) {
|
||||
packetList->writePrimitive(error);
|
||||
}
|
||||
|
||||
#endif
|
89
assignment-client/src/assets/SendAssetTask.cpp
Normal file
89
assignment-client/src/assets/SendAssetTask.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// SendAssetTask.cpp
|
||||
// assignment-client/src/assets
|
||||
//
|
||||
// Created by Ryan Huffman on 2015/08/26
|
||||
// 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 "SendAssetTask.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <NetworkLogging.h>
|
||||
#include <NLPacket.h>
|
||||
#include <NLPacketList.h>
|
||||
#include <NodeList.h>
|
||||
#include <udt/Packet.h>
|
||||
|
||||
#include "AssetUtils.h"
|
||||
|
||||
SendAssetTask::SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
|
||||
QRunnable(),
|
||||
_packet(packet),
|
||||
_senderNode(sendToNode),
|
||||
_resourcesDir(resourcesDir)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SendAssetTask::run() {
|
||||
MessageID messageID;
|
||||
uint8_t extensionLength;
|
||||
DataOffset start, end;
|
||||
|
||||
_packet->readPrimitive(&messageID);
|
||||
QByteArray assetHash = _packet->read(SHA256_HASH_LENGTH);
|
||||
_packet->readPrimitive(&extensionLength);
|
||||
QByteArray extension = _packet->read(extensionLength);
|
||||
|
||||
// `start` and `end` indicate the range of data to retrieve for the asset identified by `assetHash`.
|
||||
// `start` is inclusive, `end` is exclusive. Requesting `start` = 1, `end` = 10 will retrieve 9 bytes of data,
|
||||
// starting at index 1.
|
||||
_packet->readPrimitive(&start);
|
||||
_packet->readPrimitive(&end);
|
||||
|
||||
QString hexHash = assetHash.toHex();
|
||||
|
||||
qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end;
|
||||
|
||||
qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << messageID;
|
||||
auto replyPacketList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetGetReply, QByteArray(), true, true));
|
||||
|
||||
replyPacketList->write(assetHash);
|
||||
|
||||
replyPacketList->writePrimitive(messageID);
|
||||
|
||||
if (end <= start) {
|
||||
writeError(replyPacketList.get(), AssetServerError::InvalidByteRange);
|
||||
} else {
|
||||
QString filePath = _resourcesDir.filePath(QString(hexHash) + "." + QString(extension));
|
||||
|
||||
QFile file { filePath };
|
||||
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
if (file.size() < end) {
|
||||
writeError(replyPacketList.get(), AssetServerError::InvalidByteRange);
|
||||
qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end;
|
||||
} else {
|
||||
auto size = end - start;
|
||||
file.seek(start);
|
||||
replyPacketList->writePrimitive(AssetServerError::NoError);
|
||||
replyPacketList->writePrimitive(size);
|
||||
replyPacketList->write(file.read(size));
|
||||
qCDebug(networking) << "Sending asset: " << hexHash;
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
qCDebug(networking) << "Asset not found: " << filePath << "(" << hexHash << ")";
|
||||
writeError(replyPacketList.get(), AssetServerError::AssetNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendPacketList(std::move(replyPacketList), *_senderNode);
|
||||
}
|
38
assignment-client/src/assets/SendAssetTask.h
Normal file
38
assignment-client/src/assets/SendAssetTask.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// SendAssetTask.h
|
||||
// assignment-client/src/assets
|
||||
//
|
||||
// Created by Ryan Huffman on 2015/08/26
|
||||
// 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_SendAssetTask_h
|
||||
#define hifi_SendAssetTask_h
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QRunnable>
|
||||
|
||||
#include "AssetUtils.h"
|
||||
#include "AssetServer.h"
|
||||
#include "Node.h"
|
||||
|
||||
class NLPacket;
|
||||
|
||||
class SendAssetTask : public QRunnable {
|
||||
public:
|
||||
SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
QSharedPointer<NLPacket> _packet;
|
||||
SharedNodePointer _senderNode;
|
||||
QDir _resourcesDir;
|
||||
};
|
||||
|
||||
#endif
|
80
assignment-client/src/assets/UploadAssetTask.cpp
Normal file
80
assignment-client/src/assets/UploadAssetTask.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// UploadAssetTask.cpp
|
||||
// assignment-client/src/assets
|
||||
//
|
||||
// Created by Stephen Birarda on 2015-08-28.
|
||||
// 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 "UploadAssetTask.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
#include <AssetUtils.h>
|
||||
#include <NodeList.h>
|
||||
#include <NLPacketList.h>
|
||||
|
||||
|
||||
UploadAssetTask::UploadAssetTask(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode,
|
||||
const QDir& resourcesDir) :
|
||||
_packetList(packetList),
|
||||
_senderNode(senderNode),
|
||||
_resourcesDir(resourcesDir)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UploadAssetTask::run() {
|
||||
auto data = _packetList->getMessage();
|
||||
|
||||
QBuffer buffer { &data };
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
MessageID messageID;
|
||||
buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
|
||||
|
||||
uint8_t extensionLength;
|
||||
buffer.read(reinterpret_cast<char*>(&extensionLength), sizeof(extensionLength));
|
||||
|
||||
QByteArray extension = buffer.read(extensionLength);
|
||||
|
||||
uint64_t fileSize;
|
||||
buffer.read(reinterpret_cast<char*>(&fileSize), sizeof(fileSize));
|
||||
|
||||
qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes and extension" << extension << "from"
|
||||
<< uuidStringWithoutCurlyBraces(_senderNode->getUUID());
|
||||
|
||||
auto replyPacket = NLPacket::create(PacketType::AssetUploadReply);
|
||||
replyPacket->writePrimitive(messageID);
|
||||
|
||||
if (fileSize > MAX_UPLOAD_SIZE) {
|
||||
replyPacket->writePrimitive(AssetServerError::AssetTooLarge);
|
||||
} else {
|
||||
QByteArray fileData = buffer.read(fileSize);
|
||||
|
||||
auto hash = hashData(fileData);
|
||||
auto hexHash = hash.toHex();
|
||||
|
||||
qDebug() << "Hash for uploaded file from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID())
|
||||
<< "is: (" << hexHash << ") ";
|
||||
|
||||
QFile file { _resourcesDir.filePath(QString(hexHash)) + "." + QString(extension) };
|
||||
|
||||
if (file.exists()) {
|
||||
qDebug() << "[WARNING] This file already exists: " << hexHash;
|
||||
} else {
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(fileData);
|
||||
file.close();
|
||||
}
|
||||
replyPacket->writePrimitive(AssetServerError::NoError);
|
||||
replyPacket->write(hash);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendPacket(std::move(replyPacket), *_senderNode);
|
||||
}
|
37
assignment-client/src/assets/UploadAssetTask.h
Normal file
37
assignment-client/src/assets/UploadAssetTask.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// UploadAssetTask.h
|
||||
// assignment-client/src/assets
|
||||
//
|
||||
// Created by Stephen Birarda on 2015-08-28.
|
||||
// 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef hifi_UploadAssetTask_h
|
||||
#define hifi_UploadAssetTask_h
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QRunnable>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
class NLPacketList;
|
||||
class Node;
|
||||
|
||||
class UploadAssetTask : public QRunnable {
|
||||
public:
|
||||
UploadAssetTask(QSharedPointer<NLPacketList> packetList, QSharedPointer<Node> senderNode, const QDir& resourcesDir);
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
QSharedPointer<NLPacketList> _packetList;
|
||||
QSharedPointer<Node> _senderNode;
|
||||
QDir _resourcesDir;
|
||||
};
|
||||
|
||||
#endif // hifi_UploadAssetTask_h
|
|
@ -96,14 +96,11 @@ AudioMixer::AudioMixer(NLPacket& packet) :
|
|||
// 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.registerListenerForTypes({ PacketType::MicrophoneAudioNoEcho, PacketType::MicrophoneAudioWithEcho,
|
||||
PacketType::InjectAudio, PacketType::SilentAudioFrame,
|
||||
PacketType::AudioStreamStats },
|
||||
this, "handleNodeAudioPacket");
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
|
||||
}
|
||||
|
||||
|
@ -652,9 +649,6 @@ void AudioMixer::run() {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// we do not want this event loop to be the handler for UDP datagrams, so disconnect
|
||||
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
|
||||
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
nodeList->linkedDataCreateCallback = [](Node* node) {
|
||||
|
@ -672,7 +666,7 @@ void AudioMixer::run() {
|
|||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||
domainHandler.requestDomainSettings();
|
||||
loop.exec();
|
||||
|
||||
|
||||
if (domainHandler.getSettingsObject().isEmpty()) {
|
||||
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
||||
setFinished(true);
|
||||
|
|
|
@ -50,7 +50,7 @@ AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const {
|
|||
}
|
||||
|
||||
int AudioMixerClientData::parseData(NLPacket& packet) {
|
||||
PacketType::Value packetType = packet.getType();
|
||||
PacketType packetType = packet.getType();
|
||||
|
||||
if (packetType == PacketType::AudioStreamStats) {
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ AvatarAudioStream::AvatarAudioStream(bool isStereo, const InboundAudioStream::Se
|
|||
{
|
||||
}
|
||||
|
||||
int AvatarAudioStream::parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
||||
int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
||||
int readBytes = 0;
|
||||
|
||||
if (type == PacketType::SilentAudioFrame) {
|
||||
|
|
|
@ -25,7 +25,7 @@ private:
|
|||
AvatarAudioStream(const AvatarAudioStream&);
|
||||
AvatarAudioStream& operator= (const AvatarAudioStream&);
|
||||
|
||||
int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
|
||||
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarAudioStream_h
|
||||
|
|
|
@ -259,8 +259,8 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
return;
|
||||
}
|
||||
|
||||
PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
||||
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData);
|
||||
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
||||
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
|
||||
|
||||
if (lastSeqToReceiver > lastSeqFromSender) {
|
||||
// Did we somehow get out of order packets from the sender?
|
||||
|
@ -289,7 +289,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
// set the last sent sequence number for this sender on the receiver
|
||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||
otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData));
|
||||
otherNodeData->getLastReceivedSequenceNumber());
|
||||
|
||||
// start a new segment in the PacketList for this avatar
|
||||
avatarPacketList.startSegment();
|
||||
|
@ -539,6 +539,7 @@ void AvatarMixer::run() {
|
|||
qDebug() << "Waiting for domain settings from domain-server.";
|
||||
|
||||
// block until we get the settingsRequestComplete signal
|
||||
|
||||
QEventLoop loop;
|
||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#include "AvatarMixerClientData.h"
|
||||
|
||||
int AvatarMixerClientData::parseData(NLPacket& packet) {
|
||||
// pull the sequence number from the data first
|
||||
packet.readPrimitive(&_lastReceivedSequenceNumber);
|
||||
|
||||
// compute the offset to the data payload
|
||||
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
|
||||
}
|
||||
|
@ -24,13 +27,13 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
|
|||
return oldValue;
|
||||
}
|
||||
|
||||
PacketSequenceNumber AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
|
||||
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
|
||||
// return the matching PacketSequenceNumber, or the default if we don't have it
|
||||
auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID);
|
||||
if (nodeMatch != _lastBroadcastSequenceNumbers.end()) {
|
||||
return nodeMatch->second;
|
||||
} else {
|
||||
return DEFAULT_SEQUENCE_NUMBER;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,10 +36,12 @@ public:
|
|||
|
||||
bool checkAndSetHasReceivedFirstPackets();
|
||||
|
||||
PacketSequenceNumber getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
|
||||
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber)
|
||||
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
|
||||
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
|
||||
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
||||
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
||||
|
||||
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
||||
|
||||
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
||||
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
||||
|
@ -77,8 +79,9 @@ public:
|
|||
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||
private:
|
||||
AvatarData _avatar;
|
||||
|
||||
std::unordered_map<QUuid, PacketSequenceNumber, UUIDHasher> _lastBroadcastSequenceNumbers;
|
||||
|
||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||
|
||||
bool _hasReceivedFirstPackets = false;
|
||||
quint64 _billboardChangeTimestamp = 0;
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
OctreeQueryNode(),
|
||||
_lastDeletedEntitiesSentAt(0) { }
|
||||
|
||||
virtual PacketType::Value getMyPacketType() const { return PacketType::EntityData; }
|
||||
virtual PacketType getMyPacketType() const { return PacketType::EntityData; }
|
||||
|
||||
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
|
||||
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
||||
|
|
|
@ -28,11 +28,11 @@ public:
|
|||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
|
||||
virtual PacketType::Value getMyEditNackType() const { return PacketType::EntityEditNack; }
|
||||
virtual PacketType getMyEditNackType() const { return PacketType::EntityEditNack; }
|
||||
virtual QString getMyDomainSettingsKey() const { return QString("entity_server_settings"); }
|
||||
|
||||
// subclass may implement these method
|
||||
|
|
|
@ -89,7 +89,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
}
|
||||
|
||||
// Ask our tree subclass if it can handle the incoming packet...
|
||||
PacketType::Value packetType = packet->getType();
|
||||
PacketType packetType = packet->getType();
|
||||
|
||||
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
|
||||
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
|
||||
|
@ -128,7 +128,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
}
|
||||
|
||||
if (debugProcessPacket) {
|
||||
qDebug() << " numBytesPacketHeader=" << packet->totalHeadersSize();
|
||||
qDebug() << " numBytesPacketHeader=" << NLPacket::totalHeaderSize(packetType);
|
||||
qDebug() << " sizeof(sequence)=" << sizeof(sequence);
|
||||
qDebug() << " sizeof(sentAt)=" << sizeof(sentAt);
|
||||
qDebug() << " atByte (in payload)=" << packet->pos();
|
||||
|
@ -150,7 +150,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
if (debugProcessPacket) {
|
||||
qDebug() << " --- inside while loop ---";
|
||||
qDebug() << " maxSize=" << maxSize;
|
||||
qDebug("OctreeInboundPacketProcessor::processPacket() %c "
|
||||
qDebug("OctreeInboundPacketProcessor::processPacket() %hhu "
|
||||
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
|
||||
packetType, packet->getPayload(), packet->getPayloadSize(), editData,
|
||||
packet->pos(), maxSize);
|
||||
|
@ -188,7 +188,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
}
|
||||
|
||||
if (debugProcessPacket) {
|
||||
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c "
|
||||
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu "
|
||||
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
|
||||
packetType, packet->getPayload(), packet->getPayloadSize(), editData, packet->pos());
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
|
|||
}
|
||||
trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
|
||||
} else {
|
||||
qDebug("unknown packet ignored... packetType=%d", packetType);
|
||||
qDebug("unknown packet ignored... packetType=%hhu", packetType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ OctreeQueryNode::OctreeQueryNode() :
|
|||
_viewSent(false),
|
||||
_octreePacket(),
|
||||
_octreePacketWaiting(false),
|
||||
_lastOctreePayload(new char[MAX_PACKET_SIZE]),
|
||||
_lastOctreePayload(new char[udt::MAX_PACKET_SIZE]),
|
||||
_lastOctreePacketLength(0),
|
||||
_duplicatePacketCount(0),
|
||||
_firstSuppressedPacket(usecTimestampNow()),
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
virtual ~OctreeQueryNode();
|
||||
|
||||
void init(); // called after creation to set up some virtual items
|
||||
virtual PacketType::Value getMyPacketType() const = 0;
|
||||
virtual PacketType getMyPacketType() const = 0;
|
||||
|
||||
void resetOctreePacket(); // resets octree packet to after "V" header
|
||||
|
||||
|
@ -150,7 +150,7 @@ private:
|
|||
|
||||
quint64 _lastRootTimestamp;
|
||||
|
||||
PacketType::Value _myPacketType;
|
||||
PacketType _myPacketType;
|
||||
bool _isShuttingDown;
|
||||
|
||||
SentPacketHistory _sentPacketHistory;
|
||||
|
|
|
@ -219,7 +219,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
packetSent = true;
|
||||
|
||||
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
|
||||
thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
|
||||
thisWastedBytes = udt::MAX_PACKET_SIZE - packetSizeWithHeader;
|
||||
_totalWastedBytes += thisWastedBytes;
|
||||
_totalBytes += nodeData->getPacket().getDataSize();
|
||||
_totalPackets++;
|
||||
|
@ -251,7 +251,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
packetSent = true;
|
||||
|
||||
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
|
||||
int thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
|
||||
int thisWastedBytes = udt::MAX_PACKET_SIZE - packetSizeWithHeader;
|
||||
_totalWastedBytes += thisWastedBytes;
|
||||
_totalBytes += packetSizeWithHeader;
|
||||
_totalPackets++;
|
||||
|
@ -598,7 +598,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
|
||||
_totalBytes += packet->getDataSize();
|
||||
_totalPackets++;
|
||||
_totalWastedBytes += MAX_PACKET_SIZE - packet->getDataSize();
|
||||
_totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,11 +63,11 @@ public:
|
|||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
|
||||
virtual char getMyNodeType() const = 0;
|
||||
virtual PacketType::Value getMyQueryMessageType() const = 0;
|
||||
virtual PacketType getMyQueryMessageType() const = 0;
|
||||
virtual const char* getMyServerName() const = 0;
|
||||
virtual const char* getMyLoggingServerTargetName() const = 0;
|
||||
virtual const char* getMyDefaultPersistFilename() const = 0;
|
||||
virtual PacketType::Value getMyEditNackType() const = 0;
|
||||
virtual PacketType getMyEditNackType() const = 0;
|
||||
virtual QString getMyDomainSettingsKey() const { return QString("octree_server_settings"); }
|
||||
|
||||
// subclass may implement these method
|
||||
|
|
|
@ -73,7 +73,6 @@ endfunction()
|
|||
|
||||
|
||||
macro(AUTOSCRIBE_SHADER_LIB)
|
||||
message("Autoscribe running for ${TARGET_NAME}")
|
||||
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
|
||||
foreach(HIFI_LIBRARY ${ARGN})
|
||||
#if (NOT TARGET ${HIFI_LIBRARY})
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
macro(SETUP_HIFI_LIBRARY)
|
||||
|
||||
project(${TARGET_NAME})
|
||||
message("Setting up project ${TARGET_NAME}")
|
||||
|
||||
|
||||
# grab the implemenation and header files
|
||||
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c")
|
||||
list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS})
|
||||
|
|
|
@ -166,6 +166,21 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "asset_server",
|
||||
"label": "Asset Server",
|
||||
"assignment-types": [3],
|
||||
"settings": [
|
||||
{
|
||||
"name": "enabled",
|
||||
"type": "checkbox",
|
||||
"label": "Enabled",
|
||||
"help": "Assigns an asset-server in your domain to serve files to clients via the ATP protocol (over UDP)",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "audio_env",
|
||||
"label": "Audio Environment",
|
||||
|
|
|
@ -48,7 +48,8 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID
|
|||
}
|
||||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::EntityServer;
|
||||
<< NodeType::AvatarMixer << NodeType::EntityServer
|
||||
<< NodeType::AssetServer;
|
||||
|
||||
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||
if (packet->getPayloadSize() == 0) {
|
||||
|
@ -65,6 +66,16 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
|
|||
return;
|
||||
}
|
||||
|
||||
static const NodeSet VALID_NODE_TYPES {
|
||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent
|
||||
};
|
||||
|
||||
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
||||
qDebug() << "Received an invalid node type with connect request. Will not allow connection from"
|
||||
<< nodeConnection.senderSockAddr;
|
||||
return;
|
||||
}
|
||||
|
||||
// check if this connect request matches an assignment in the queue
|
||||
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
||||
|
||||
|
|
|
@ -108,6 +108,11 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
}
|
||||
}
|
||||
|
||||
DomainServer::~DomainServer() {
|
||||
// destroy the LimitedNodeList before the DomainServer QCoreApplication is down
|
||||
DependencyManager::destroy<LimitedNodeList>();
|
||||
}
|
||||
|
||||
void DomainServer::aboutToQuit() {
|
||||
|
||||
// clear the log handler so that Qt doesn't call the destructor on LogHandler
|
||||
|
@ -257,7 +262,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
auto nodeList = DependencyManager::set<LimitedNodeList>(domainServerPort, domainServerDTLSPort);
|
||||
|
||||
// no matter the local port, save it to shared mem so that local assignment clients can ask what it is
|
||||
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getNodeSocket().localPort());
|
||||
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getSocketLocalPort());
|
||||
|
||||
// store our local http ports in shared memory
|
||||
quint16 localHttpPort = DOMAIN_SERVER_HTTP_PORT;
|
||||
|
@ -280,10 +285,15 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
// register as the packet receiver for the types we want
|
||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||
|
||||
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
||||
|
||||
// register the gatekeeper for the packets it needs to receive
|
||||
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
|
||||
|
@ -561,8 +571,19 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
if (!excludedTypes.contains(defaultedType)
|
||||
&& defaultedType != Assignment::UNUSED_0
|
||||
&& defaultedType != Assignment::UNUSED_1
|
||||
&& defaultedType != Assignment::UNUSED_2
|
||||
&& defaultedType != Assignment::AgentType) {
|
||||
|
||||
if (defaultedType == Assignment::AssetServerType) {
|
||||
// Make sure the asset-server is enabled before adding it here.
|
||||
// Initially we do not assign it by default so we can test it in HF domains first
|
||||
static const QString ASSET_SERVER_ENABLED_KEYPATH = "asset_server.enabled";
|
||||
|
||||
if (!_settingsManager.valueOrDefaultValueForKeyPath(ASSET_SERVER_ENABLED_KEYPATH).toBool()) {
|
||||
// skip to the next iteration if asset-server isn't enabled
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// type has not been set from a command line or config file config, use the default
|
||||
// by clearing whatever exists and writing a single default assignment with no payload
|
||||
Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
|
||||
|
|
|
@ -39,7 +39,8 @@ class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
|
|||
Q_OBJECT
|
||||
public:
|
||||
DomainServer(int argc, char* argv[]);
|
||||
|
||||
~DomainServer();
|
||||
|
||||
static int const EXIT_CODE_REBOOT;
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <Assignment.h>
|
||||
#include <HifiConfigVariantMap.h>
|
||||
#include <HTTPConnection.h>
|
||||
#include <NLPacketList.h>
|
||||
|
||||
#include "DomainServerSettingsManager.h"
|
||||
|
||||
|
@ -66,6 +67,21 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
|
|||
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||
Assignment::Type type;
|
||||
packet->readPrimitive(&type);
|
||||
|
||||
QJsonObject responseObject = responseObjectForType(QString::number(type));
|
||||
auto json = QJsonDocument(responseObject).toJson();
|
||||
|
||||
auto packetList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::DomainSettings, QByteArray(), true, true));
|
||||
|
||||
packetList->write(json);
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
nodeList->sendPacketList(std::move(packetList), packet->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
_configMap.loadMasterAndUserConfig(argumentList);
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <HifiConfigVariantMap.h>
|
||||
#include <HTTPManager.h>
|
||||
|
||||
#include <NLPacket.h>
|
||||
|
||||
const QString SETTINGS_PATHS_KEY = "paths";
|
||||
|
||||
const QString SETTINGS_PATH = "/settings";
|
||||
|
@ -38,6 +40,10 @@ public:
|
|||
|
||||
QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); }
|
||||
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
||||
|
||||
private slots:
|
||||
void processSettingsRequestPacket(QSharedPointer<NLPacket> packet);
|
||||
|
||||
private:
|
||||
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
|
||||
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant);
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
//
|
||||
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
|
||||
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
|
||||
var rightTriggerAction = RIGHT_HAND_CLICK;
|
||||
|
@ -41,7 +42,7 @@ var INTERSECT_COLOR = {
|
|||
blue: 10
|
||||
};
|
||||
|
||||
var GRAB_RADIUS = 0.5;
|
||||
var GRAB_RADIUS = 1.0;
|
||||
|
||||
var GRAB_COLOR = {
|
||||
red: 250,
|
||||
|
@ -135,7 +136,8 @@ controller.prototype.checkForIntersections = function(origin, direction) {
|
|||
|
||||
var intersection = Entities.findRayIntersection(pickRay, true);
|
||||
if (intersection.intersects && intersection.properties.collisionsWillMove === 1) {
|
||||
this.distanceToEntity = Vec3.distance(origin, intersection.properties.position);
|
||||
var handPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
this.distanceToEntity = Vec3.distance(handPosition, intersection.properties.position);
|
||||
Entities.editEntity(this.pointer, {
|
||||
linePoints: [
|
||||
ZERO_VEC,
|
||||
|
@ -236,19 +238,24 @@ controller.prototype.update = function() {
|
|||
controller.prototype.grabEntity = function() {
|
||||
var handRotation = this.getHandRotation();
|
||||
var handPosition = this.getHandPosition();
|
||||
|
||||
var objectRotation = Entities.getEntityProperties(this.grabbedEntity).rotation;
|
||||
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
|
||||
|
||||
var objectPosition = Entities.getEntityProperties(this.grabbedEntity).position;
|
||||
var offset = Vec3.subtract(objectPosition, handPosition);
|
||||
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
|
||||
this.closeGrabbing = true;
|
||||
//check if our entity has instructions on how to be grabbed, otherwise, just use default relative position and rotation
|
||||
var userData = getEntityUserData(this.grabbedEntity);
|
||||
var relativePosition = ZERO_VEC;
|
||||
var relativeRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
if(userData.spatialKey) {
|
||||
if(userData.spatialKey.relativePosition) {
|
||||
relativePosition = userData.spatialKey.relativePosition;
|
||||
}
|
||||
if(userData.spatialKey.relativeRotation) {
|
||||
relativeRotation = userData.spatialKey.relativeRotation;
|
||||
}
|
||||
}
|
||||
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
||||
relativePosition: offsetPosition,
|
||||
relativeRotation: offsetRotation,
|
||||
hand: this.hand,
|
||||
timeScale: 0.05
|
||||
timeScale: 0.05,
|
||||
relativePosition: relativePosition,
|
||||
relativeRotation: relativeRotation
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -305,10 +312,10 @@ controller.prototype.onActionEvent = function(action, state) {
|
|||
self.checkForEntityArrival = true;
|
||||
}, 500);
|
||||
var handPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
var direction = Controller.getSpatialControlNormal(this.tip);
|
||||
var direction = Vec3.normalize(Controller.getSpatialControlNormal(this.tip));
|
||||
//move final destination along line a bit, so it doesnt hit avatar hand
|
||||
Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
targetPosition: Vec3.sum(handPosition, Vec3.multiply(2, direction))
|
||||
targetPosition: Vec3.sum(handPosition, Vec3.multiply(3, direction))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -339,8 +346,6 @@ function cleanup() {
|
|||
leftController.cleanup();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Script.update.connect(update)
|
||||
Controller.actionEvent.connect(onActionEvent);
|
|
@ -14,6 +14,7 @@ Script.load("selectAudioDevice.js");
|
|||
Script.load("inspect.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("users.js");
|
||||
Script.load("handControllerGrab.js");
|
||||
Script.load("grab.js");
|
||||
Script.load("directory.js");
|
||||
Script.load("dialTone.js");
|
||||
|
|
126
examples/entityScripts/boombox.js
Normal file
126
examples/entityScripts/boombox.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// boombox.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 9/3/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example of a boom box entity script which when assigned to an entity, will detect when the entity is being touched by the avatars hands
|
||||
// and start to play a boom box song
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
var _this;
|
||||
var BOOMBOX_USER_DATA_KEY = "boombox";
|
||||
var THE_SONG = "http://hifi-public.s3.amazonaws.com/ryan/freaks7.wav";
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||
DetectTouched = function() {
|
||||
_this = this;
|
||||
_this.beingTouched = false;
|
||||
_this.isPlaying = false;
|
||||
_this.injector = null;
|
||||
};
|
||||
|
||||
DetectTouched.prototype = {
|
||||
|
||||
// update() will be called regulary, because we've hooked the update signal in our preload() function.
|
||||
// we will check the avatars hand positions and if either hand is in our bounding box, we will notice that
|
||||
update: function() {
|
||||
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
||||
var entityID = _this.entityID;
|
||||
|
||||
var leftHandPosition = MyAvatar.getLeftPalmPosition();
|
||||
var rightHandPosition = MyAvatar.getRightPalmPosition();
|
||||
var props = Entities.getEntityProperties(entityID);
|
||||
var entityMinPoint = props.boundingBox.brn;
|
||||
var entityMaxPoint = props.boundingBox.tfl;
|
||||
|
||||
// this is our assumed boombox data if it's not known
|
||||
var defaultBoomboxData = { isPlaying: false, avatarID: null };
|
||||
|
||||
// this handy function getEntityCustomData() is available in utils.js and it will return just the specific section
|
||||
// of user data we asked for. If it's not available it returns our default data.
|
||||
var boomboxData = getEntityCustomData(BOOMBOX_USER_DATA_KEY, entityID, defaultBoomboxData);
|
||||
|
||||
// Only allow interaction if no one else is interacting with the boombox or if we are...
|
||||
if (!boomboxData.isPlaying || boomboxData.avatarID == MyAvatar.sessionUUID) {
|
||||
|
||||
// If we were not previously being touched, and we just got touched, then we do our touching action.
|
||||
if (pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint) || pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint)) {
|
||||
|
||||
if (!_this.beingTouched) {
|
||||
print("I was just touched...");
|
||||
|
||||
// remember we're being grabbed so we can detect being released
|
||||
_this.beingTouched = true;
|
||||
|
||||
// determine what to do based on if we are currently playing
|
||||
if (!_this.isPlaying) {
|
||||
// if we are not currently playing, then start playing.
|
||||
print("Start playing...");
|
||||
|
||||
_this.isPlaying = true;
|
||||
|
||||
if (_this.injector == null) {
|
||||
_this.injector = Audio.playSound(_this.song, {
|
||||
position: props.position, // position of boombox entity
|
||||
volume: 0.5,
|
||||
loop: true
|
||||
});
|
||||
} else {
|
||||
_this.injector.restart();
|
||||
}
|
||||
setEntityCustomData(BOOMBOX_USER_DATA_KEY, entityID, { isPlaying: true, avatarID: MyAvatar.sessionUUID });
|
||||
|
||||
} else {
|
||||
// if we are currently playing, then stop playing
|
||||
print("Stop playing...");
|
||||
_this.isPlaying = false;
|
||||
_this.injector.stop();
|
||||
|
||||
setEntityCustomData(BOOMBOX_USER_DATA_KEY, entityID, { isPlaying: false, avatarID: null });
|
||||
} // endif (!isPlaying)
|
||||
|
||||
} // endif !beingTouched
|
||||
} else if (_this.beingTouched) {
|
||||
// if we are not being grabbed, and we previously were, then we were just released, remember that
|
||||
// and print out a message
|
||||
_this.beingTouched = false;
|
||||
print("I'm am no longer being touched...");
|
||||
}
|
||||
|
||||
} else {
|
||||
// end if for -- only interact if no one else is...
|
||||
print("someone else is playing the boombox...");
|
||||
}
|
||||
},
|
||||
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
// * connecting to the update signal so we can check our grabbed state
|
||||
preload: function(entityID) {
|
||||
print("preload!");
|
||||
this.entityID = entityID;
|
||||
Script.update.connect(this.update);
|
||||
this.song = SoundCache.getSound(THE_SONG);
|
||||
},
|
||||
|
||||
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||
// to the update signal
|
||||
unload: function(entityID) {
|
||||
Script.update.disconnect(this.update);
|
||||
},
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new DetectTouched();
|
||||
})
|
|
@ -50,7 +50,8 @@
|
|||
// remember we're being grabbed so we can detect being released
|
||||
_this.beingGrabbed = true;
|
||||
var props = Entities.getEntityProperties(entityID);
|
||||
breakdanceStart(props.modelURL, props.position);
|
||||
var puppetPosition = getPuppetPosition(props);
|
||||
breakdanceStart(props.modelURL, puppetPosition);
|
||||
print("I'm was grabbed...");
|
||||
} else {
|
||||
breakdanceUpdate();
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
//
|
||||
// sprayPaintCan.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Eric Levin on 9/3/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example of an entity script for painting with a spraypaint can
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
(function() {
|
||||
Script.include("../libraries/utils.js");
|
||||
SPATIAL_USER_DATA_KEY = "spatialKey";
|
||||
this.userData = {};
|
||||
|
||||
var TIP_OFFSET_Z = 0.14;
|
||||
|
@ -36,6 +26,8 @@
|
|||
});
|
||||
|
||||
this.getUserData = function() {
|
||||
|
||||
|
||||
if (this.properties.userData) {
|
||||
this.userData = JSON.parse(this.properties.userData);
|
||||
}
|
||||
|
@ -50,8 +42,7 @@
|
|||
this.update = function(deltaTime) {
|
||||
self.properties = Entities.getEntityProperties(self.entityId);
|
||||
self.getUserData();
|
||||
//Only run the logic if this is the client whose avatar is grabbing
|
||||
if (self.userData.grabKey && self.userData.grabKey.activated === true && self.userData.grabKey.avatarId === MyAvatar.sessionUUID) {
|
||||
if (self.userData.grabKey && self.userData.grabKey.activated === true) {
|
||||
if (self.activated !== true) {
|
||||
Entities.editEntity(self.paintStream, {
|
||||
animationSettings: startSetting
|
||||
|
@ -70,6 +61,8 @@
|
|||
|
||||
this.sprayStream = function() {
|
||||
var forwardVec = Quat.getFront(self.properties.rotation);
|
||||
forwardVec = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 90, 0), forwardVec);
|
||||
|
||||
var upVec = Quat.getUp(self.properties.rotation);
|
||||
var position = Vec3.sum(self.properties.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
|
||||
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
|
||||
|
@ -92,11 +85,12 @@
|
|||
var normal = Vec3.multiply(-1, Quat.getFront(intersection.properties.rotation));
|
||||
this.paint(intersection.intersection, normal);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.paint = function(position, normal) {
|
||||
if (!this.painting) {
|
||||
print("position " + JSON.stringify(position))
|
||||
|
||||
this.newStroke(position);
|
||||
this.painting = true;
|
||||
|
@ -125,6 +119,7 @@
|
|||
strokeWidths: this.strokeWidths
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.newStroke = function(position) {
|
||||
|
@ -157,10 +152,16 @@
|
|||
this.entityId = entityId;
|
||||
this.properties = Entities.getEntityProperties(self.entityId);
|
||||
this.getUserData();
|
||||
print("USER DATA " + JSON.stringify(this.userData))
|
||||
if (this.userData.activated) {
|
||||
if (this.userData.grabKey && this.userData.grabKey.activated) {
|
||||
this.activated = true;
|
||||
}
|
||||
if(!this.userData.spatialKey) {
|
||||
var data = {
|
||||
relativePosition: {x: 0, y: 0, z: 0},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0,0)
|
||||
}
|
||||
setEntityCustomData(SPATIAL_USER_DATA_KEY, this.entityId, data);
|
||||
}
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
|
@ -173,6 +174,7 @@
|
|||
running: false
|
||||
});
|
||||
|
||||
|
||||
this.paintStream = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
animationSettings: animationSettings,
|
||||
|
|
|
@ -11,7 +11,47 @@
|
|||
//
|
||||
|
||||
|
||||
function getPositionPuppet() {
|
||||
function getPuppetPosition(props) {
|
||||
var MAX_DISTANCE = 10;
|
||||
var searchPosition = MyAvatar.position;
|
||||
|
||||
if (props !== undefined && props.position !== undefined) {
|
||||
searchPosition = props.position;
|
||||
}
|
||||
|
||||
// first look for the "Table 1" entity
|
||||
var ids = Entities.findEntities(searchPosition, MAX_DISTANCE);
|
||||
|
||||
for (var i in ids) {
|
||||
var entityId = ids[i];
|
||||
var foundProps = Entities.getEntityProperties(entityId);
|
||||
if (foundProps.name == "Table 1") {
|
||||
// we want to keep the puppet to be bound to the x/z bouds of the table
|
||||
// and placed at the top of the table suface. But we want to generally position
|
||||
// the puppet at the x/z of where the toy was grabbed.
|
||||
|
||||
var minX = foundProps.boundingBox.brn.x + (props.dimensions.x / 2);
|
||||
var maxX = foundProps.boundingBox.tfl.x - (props.dimensions.x / 2);
|
||||
var minZ = foundProps.boundingBox.brn.z + (props.dimensions.z / 2);
|
||||
var maxZ = foundProps.boundingBox.tfl.z - (props.dimensions.z / 2);
|
||||
|
||||
var bestX = Math.min(maxX, Math.max(props.position.x, minX));
|
||||
var bestZ = Math.min(maxZ, Math.max(props.position.z, minZ));
|
||||
|
||||
// Adjust the position to be the "top" of the table. Note: this assumes the table has registrationPoint of 0.5
|
||||
// this also assumes the table hasn't been rotated in such a manner that the table top isn't flat
|
||||
var bestY = foundProps.boundingBox.center.y + (foundProps.boundingBox.dimensions.y / 2);
|
||||
|
||||
position = { x: bestX, y: bestY, z: bestZ };
|
||||
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
if (props !== undefined && props.position !== undefined) {
|
||||
return props.position;
|
||||
}
|
||||
|
||||
var DISTANCE_IN_FRONT = 2;
|
||||
var DISTANCE_UP = 0.4;
|
||||
var DISTANCE_TO_SIDE = 0.0;
|
||||
|
@ -27,11 +67,10 @@ function getPositionPuppet() {
|
|||
|
||||
var offset = Vec3.sum(Vec3.sum(leftOffset, frontOffset), upOffset);
|
||||
var position = Vec3.sum(MyAvatar.position, offset);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getPositionLeftFront() {
|
||||
var DISTANCE_IN_FRONT = 0.6;
|
||||
var DISTANCE_UP = 0.4;
|
||||
|
@ -363,11 +402,12 @@ function createPuppet(model, location) {
|
|||
model = "https://hifi-public.s3.amazonaws.com/models/Bboys/bboy1/bboy1.fbx";
|
||||
}
|
||||
if (location == undefined) {
|
||||
location = getPositionPuppet();
|
||||
location = getPuppetPosition();
|
||||
}
|
||||
puppetEntityID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: model,
|
||||
registrationPoint: { x: 0.5, y: 0, z: 0.5 },
|
||||
animationURL: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx",
|
||||
animationSettings: ANIMATION_SETTINGS,
|
||||
position: location,
|
||||
|
|
|
@ -34,8 +34,12 @@ IceServer::IceServer(int argc, char* argv[]) :
|
|||
qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT;
|
||||
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
|
||||
|
||||
// call our process datagrams slot when the UDP socket has packets ready
|
||||
connect(&_serverSocket, &QUdpSocket::readyRead, this, &IceServer::processDatagrams);
|
||||
// set processPacket as the verified packet callback for the udt::Socket
|
||||
_serverSocket.setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); });
|
||||
|
||||
// set packetVersionMatch as the verify packet operator for the udt::Socket
|
||||
using std::placeholders::_1;
|
||||
_serverSocket.setPacketFilterOperator(std::bind(&IceServer::packetVersionMatch, this, _1));
|
||||
|
||||
// setup our timer to clear inactive peers
|
||||
QTimer* inactivePeerTimer = new QTimer(this);
|
||||
|
@ -44,67 +48,69 @@ IceServer::IceServer(int argc, char* argv[]) :
|
|||
|
||||
}
|
||||
|
||||
void IceServer::processDatagrams() {
|
||||
HifiSockAddr sendingSockAddr;
|
||||
|
||||
while (_serverSocket.hasPendingDatagrams()) {
|
||||
// setup a buffer to read the packet into
|
||||
int packetSizeWithHeader = _serverSocket.pendingDatagramSize();
|
||||
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
|
||||
|
||||
_serverSocket.readDatagram(buffer.get(), packetSizeWithHeader,
|
||||
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
|
||||
bool IceServer::packetVersionMatch(const udt::Packet& packet) {
|
||||
PacketType headerType = NLPacket::typeInHeader(packet);
|
||||
PacketVersion headerVersion = NLPacket::versionInHeader(packet);
|
||||
|
||||
if (headerVersion == versionForPacketType(headerType)) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Packet version mismatch for packet" << headerType
|
||||
<< "(" << nameForPacketType(headerType) << ") from" << packet.getSenderSockAddr();
|
||||
|
||||
// make sure that this packet at least looks like something we can read
|
||||
if (packetSizeWithHeader >= Packet::localHeaderSize(PacketType::ICEServerHeartbeat)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void IceServer::processPacket(std::unique_ptr<udt::Packet> packet) {
|
||||
|
||||
auto nlPacket = NLPacket::fromBase(std::move(packet));
|
||||
|
||||
// make sure that this packet at least looks like something we can read
|
||||
if (nlPacket->getPayloadSize() >= NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) {
|
||||
|
||||
if (nlPacket->getType() == PacketType::ICEServerHeartbeat) {
|
||||
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket);
|
||||
|
||||
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, sendingSockAddr);
|
||||
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
|
||||
peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr());
|
||||
} else if (nlPacket->getType() == PacketType::ICEServerQuery) {
|
||||
QDataStream heartbeatStream(nlPacket.get());
|
||||
|
||||
PacketType::Value packetType = packet->getType();
|
||||
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
|
||||
QUuid senderUUID;
|
||||
heartbeatStream >> senderUUID;
|
||||
|
||||
if (packetType == PacketType::ICEServerHeartbeat) {
|
||||
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*packet);
|
||||
// pull the public and private sock addrs for this peer
|
||||
HifiSockAddr publicSocket, localSocket;
|
||||
heartbeatStream >> publicSocket >> localSocket;
|
||||
|
||||
// check if this node also included a UUID that they would like to connect to
|
||||
QUuid connectRequestID;
|
||||
heartbeatStream >> connectRequestID;
|
||||
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
|
||||
|
||||
if (matchingPeer) {
|
||||
|
||||
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
|
||||
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
|
||||
} else if (packetType == PacketType::ICEServerQuery) {
|
||||
QDataStream heartbeatStream(packet.get());
|
||||
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
|
||||
|
||||
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
|
||||
QUuid senderUUID;
|
||||
heartbeatStream >> senderUUID;
|
||||
// we have the peer they want to connect to - send them pack the information for that peer
|
||||
sendPeerInformationPacket(*matchingPeer, &nlPacket->getSenderSockAddr());
|
||||
|
||||
// pull the public and private sock addrs for this peer
|
||||
HifiSockAddr publicSocket, localSocket;
|
||||
heartbeatStream >> publicSocket >> localSocket;
|
||||
// we also need to send them to the active peer they are hoping to connect to
|
||||
// create a dummy peer object we can pass to sendPeerInformationPacket
|
||||
|
||||
// check if this node also included a UUID that they would like to connect to
|
||||
QUuid connectRequestID;
|
||||
heartbeatStream >> connectRequestID;
|
||||
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
|
||||
|
||||
if (matchingPeer) {
|
||||
|
||||
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
|
||||
|
||||
// we have the peer they want to connect to - send them pack the information for that peer
|
||||
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
|
||||
|
||||
// we also need to send them to the active peer they are hoping to connect to
|
||||
// create a dummy peer object we can pass to sendPeerInformationPacket
|
||||
|
||||
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
|
||||
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
|
||||
} else {
|
||||
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
|
||||
}
|
||||
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
|
||||
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
|
||||
} else {
|
||||
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(Packet& packet) {
|
||||
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(NLPacket& packet) {
|
||||
|
||||
// pull the UUID, public and private sock addrs for this peer
|
||||
QUuid senderUUID;
|
||||
|
@ -137,14 +143,13 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(Packet& packet) {
|
|||
}
|
||||
|
||||
void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {
|
||||
auto peerPacket = Packet::create(PacketType::ICEServerPeerInformation);
|
||||
auto peerPacket = NLPacket::create(PacketType::ICEServerPeerInformation);
|
||||
|
||||
// get the byte array for this peer
|
||||
peerPacket->write(peer.toByteArray());
|
||||
|
||||
// write the current packet
|
||||
_serverSocket.writeDatagram(peerPacket->getData(), peerPacket->getDataSize(),
|
||||
destinationSockAddr->getAddress(), destinationSockAddr->getPort());
|
||||
_serverSocket.writePacket(*peerPacket, *destinationSockAddr);
|
||||
}
|
||||
|
||||
void IceServer::clearInactivePeers() {
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
#include <NetworkPeer.h>
|
||||
#include <HTTPConnection.h>
|
||||
#include <HTTPManager.h>
|
||||
#include <udt/Packet.h>
|
||||
#include <NLPacket.h>
|
||||
#include <udt/Socket.h>
|
||||
|
||||
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
|
||||
|
||||
|
@ -29,15 +30,16 @@ public:
|
|||
IceServer(int argc, char* argv[]);
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||
private slots:
|
||||
void processDatagrams();
|
||||
void clearInactivePeers();
|
||||
private:
|
||||
|
||||
SharedNetworkPeer addOrUpdateHeartbeatingPeer(Packet& incomingPacket);
|
||||
bool packetVersionMatch(const udt::Packet& packet);
|
||||
void processPacket(std::unique_ptr<udt::Packet> packet);
|
||||
|
||||
SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket);
|
||||
void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
|
||||
|
||||
QUuid _id;
|
||||
QUdpSocket _serverSocket;
|
||||
udt::Socket _serverSocket;
|
||||
NetworkPeerHash _activePeers;
|
||||
HTTPManager _httpManager;
|
||||
};
|
||||
|
|
|
@ -28,39 +28,39 @@ Item {
|
|||
anchors.fill: parent
|
||||
onClicked: { root.expanded = !root.expanded; }
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
id: generalCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Servers: " + root.serverCount
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Avatars: " + root.avatarCount
|
||||
text: "Avatars: " + root.avatarCount
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Framerate: " + root.framerate
|
||||
text: "Framerate: " + root.framerate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Simrate: " + root.simrate
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
|
||||
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
|
||||
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,30 +77,35 @@ Item {
|
|||
Column {
|
||||
id: pingCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Audio ping: " + root.audioPing
|
||||
text: "Audio ping: " + root.audioPing
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Avatar ping: " + root.avatarPing
|
||||
text: "Avatar ping: " + root.avatarPing
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Entities avg ping: " + root.entitiesPing
|
||||
text: "Entities avg ping: " + root.entitiesPing
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "Asset ping: " + root.assetPing
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "Voxel max ping: " + 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
width: geoCol.width + 8
|
||||
height: geoCol.height + 8
|
||||
|
@ -112,34 +117,34 @@ Item {
|
|||
Column {
|
||||
id: geoCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Position: " + root.position.x.toFixed(1) + ", " +
|
||||
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
|
||||
text: "Position: " + root.position.x.toFixed(1) + ", " +
|
||||
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Velocity: " + root.velocity.toFixed(1)
|
||||
text: "Velocity: " + root.velocity.toFixed(1)
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Yaw: " + root.yaw.toFixed(1)
|
||||
text: "Yaw: " + root.yaw.toFixed(1)
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " +
|
||||
root.avatarMixerPps + "pps";
|
||||
visible: root.expanded;
|
||||
text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " +
|
||||
root.avatarMixerPps + "pps";
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "Downloads: ";
|
||||
visible: root.expanded;
|
||||
text: "Downloads: ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,72 +159,72 @@ Item {
|
|||
Column {
|
||||
id: octreeCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Triangles: " + root.triangles +
|
||||
" / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches
|
||||
text: "Triangles: " + root.triangles +
|
||||
" / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque +
|
||||
" / Translucent: " + root.meshTranslucent;
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded;
|
||||
text: "\tOpaque considered: " + root.opaqueConsidered +
|
||||
" / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall;
|
||||
text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque +
|
||||
" / Translucent: " + root.meshTranslucent;
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: !root.expanded
|
||||
text: "Octree Elements Server: " + root.serverElements +
|
||||
" Local: " + root.localElements;
|
||||
visible: root.expanded;
|
||||
text: "\tOpaque considered: " + root.opaqueConsidered +
|
||||
" / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall;
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "Octree Sending Mode: " + root.sendingMode;
|
||||
visible: !root.expanded
|
||||
text: "Octree Elements Server: " + root.serverElements +
|
||||
" Local: " + root.localElements;
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "Octree Packets to Process: " + root.packetStats;
|
||||
visible: root.expanded
|
||||
text: "Octree Sending Mode: " + root.sendingMode;
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "Octree Elements - ";
|
||||
visible: root.expanded
|
||||
text: "Octree Packets to Process: " + root.packetStats;
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "\tServer: " + root.serverElements +
|
||||
" Internal: " + root.serverInternal +
|
||||
" Leaves: " + root.serverLeaves;
|
||||
visible: root.expanded
|
||||
text: "Octree Elements - ";
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "\tLocal: " + root.localElements +
|
||||
" Internal: " + root.localInternal +
|
||||
" Leaves: " + root.localLeaves;
|
||||
visible: root.expanded
|
||||
text: "\tServer: " + root.serverElements +
|
||||
" Internal: " + root.serverInternal +
|
||||
" Leaves: " + root.serverLeaves;
|
||||
}
|
||||
Text {
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "LOD: " + root.lodStatus;
|
||||
visible: root.expanded
|
||||
text: "\tLocal: " + root.localElements +
|
||||
" Internal: " + root.localInternal +
|
||||
" Leaves: " + root.localLeaves;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "LOD: " + root.lodStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <AssetClient.h>
|
||||
#include <ApplicationVersion.h>
|
||||
#include <CursorManager.h>
|
||||
#include <AudioInjector.h>
|
||||
|
@ -310,6 +311,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
auto autoUpdater = DependencyManager::set<AutoUpdater>();
|
||||
auto pathUtils = DependencyManager::set<PathUtils>();
|
||||
auto actionFactory = DependencyManager::set<InterfaceActionFactory>();
|
||||
auto assetClient = DependencyManager::set<AssetClient>();
|
||||
auto userInputMapper = DependencyManager::set<UserInputMapper>();
|
||||
|
||||
return true;
|
||||
|
@ -458,6 +460,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
audioThread->start();
|
||||
|
||||
QThread* assetThread = new QThread;
|
||||
|
||||
assetThread->setObjectName("Asset Thread");
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
|
||||
assetClient->moveToThread(assetThread);
|
||||
|
||||
assetThread->start();
|
||||
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
||||
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
|
@ -487,8 +498,7 @@ 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->getPacketReceiver(), &PacketReceiver::packetVersionMismatch,
|
||||
this, &Application::notifyPacketVersionMismatch);
|
||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
|
||||
|
||||
// connect to appropriate slots on AccountManager
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
@ -528,7 +538,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
// tell the NodeList instance who to tell the domain server we care about
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||
<< NodeType::EntityServer);
|
||||
<< NodeType::EntityServer << NodeType::AssetServer);
|
||||
|
||||
// connect to the packet sent signal of the _entityEditSender
|
||||
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
|
||||
|
@ -878,6 +888,12 @@ Application::~Application() {
|
|||
DependencyManager::destroy<GeometryCache>();
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
|
||||
// cleanup the AssetClient thread
|
||||
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
|
||||
DependencyManager::destroy<AssetClient>();
|
||||
assetThread->quit();
|
||||
assetThread->wait();
|
||||
|
||||
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
|
||||
|
||||
|
@ -887,7 +903,7 @@ Application::~Application() {
|
|||
// ask the node thread to quit and wait until it is done
|
||||
nodeThread->quit();
|
||||
nodeThread->wait();
|
||||
|
||||
|
||||
Leapmotion::destroy();
|
||||
RealSense::destroy();
|
||||
ConnexionClient::getInstance().destroy();
|
||||
|
@ -2040,6 +2056,7 @@ void Application::sendPingPackets() {
|
|||
case NodeType::AvatarMixer:
|
||||
case NodeType::AudioMixer:
|
||||
case NodeType::EntityServer:
|
||||
case NodeType::AssetServer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -3012,7 +3029,7 @@ int Application::sendNackPackets() {
|
|||
return packetsSent;
|
||||
}
|
||||
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType::Value packetType, NodeToJurisdictionMap& jurisdictions) {
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) {
|
||||
|
||||
//qCDebug(interfaceapp) << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView();
|
||||
bool wantExtraDebugging = getLogger()->extraDebugging();
|
||||
|
@ -3776,6 +3793,9 @@ void Application::nodeAdded(SharedNodePointer node) {
|
|||
if (node->getType() == NodeType::AvatarMixer) {
|
||||
// new avatar mixer, send off our identity packet right away
|
||||
_myAvatar->sendIdentityPacket();
|
||||
} else if (node->getType() == NodeType::AssetServer) {
|
||||
// the addition of an asset-server always re-enables the upload to asset server menu option
|
||||
Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3823,6 +3843,10 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
} else if (node->getType() == NodeType::AvatarMixer) {
|
||||
// our avatar mixer has gone away - clear the hash of avatars
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
} else if (node->getType() == NodeType::AssetServer
|
||||
&& !DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AssetServer)) {
|
||||
// this was our last asset server - disable the menu option to upload an asset
|
||||
Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -492,7 +492,7 @@ private:
|
|||
|
||||
void renderLookatIndicator(glm::vec3 pointOfInterest);
|
||||
|
||||
void queryOctree(NodeType_t serverType, PacketType::Value packetType, NodeToJurisdictionMap& jurisdictions);
|
||||
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
|
||||
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
|
||||
glm::vec3 getSunDirection();
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <UserActivityLogger.h>
|
||||
#include <VrMenu.h>
|
||||
|
||||
|
||||
#include "Application.h"
|
||||
#include "AccountManager.h"
|
||||
#include "audio/AudioScope.h"
|
||||
|
@ -33,13 +32,15 @@
|
|||
#include "devices/3DConnexionClient.h"
|
||||
#include "MainWindow.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
#include "SpeechRecognizer.h"
|
||||
#endif
|
||||
#include "ui/AssetUploadDialogFactory.h"
|
||||
#include "ui/DialogsManager.h"
|
||||
#include "ui/StandAloneJSConsole.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
#include "SpeechRecognizer.h"
|
||||
#endif
|
||||
|
||||
#include "Menu.h"
|
||||
|
||||
Menu* Menu::_instance = NULL;
|
||||
|
@ -353,7 +354,21 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
|
||||
|
||||
MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets");
|
||||
|
||||
auto& assetDialogFactory = AssetUploadDialogFactory::getInstance();
|
||||
assetDialogFactory.setParent(this);
|
||||
|
||||
QAction* assetUpload = addActionToQMenuAndActionHash(assetDeveloperMenu,
|
||||
MenuOption::UploadAsset,
|
||||
0,
|
||||
&assetDialogFactory,
|
||||
SLOT(showDialog()));
|
||||
|
||||
// disable the asset upload action by default - it gets enabled only if asset server becomes present
|
||||
assetUpload->setEnabled(false);
|
||||
|
||||
MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
|
||||
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
|
||||
|
|
|
@ -287,6 +287,7 @@ namespace MenuOption {
|
|||
const QString ToolWindow = "Tool Window";
|
||||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString UploadAsset = "Upload File to Asset Server";
|
||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||
const QString UseCamera = "Use Camera";
|
||||
const QString VelocityFilter = "Velocity Filter";
|
||||
|
|
|
@ -735,7 +735,7 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) {
|
|||
_enableDebugDrawAnimPose = isEnabled;
|
||||
|
||||
if (!isEnabled) {
|
||||
AnimDebugDraw::getInstance().removeAnimNode("myAvatar");
|
||||
AnimDebugDraw::getInstance().removePoses("myAvatar");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1248,8 +1248,6 @@ void MyAvatar::initAnimGraph() {
|
|||
|
||||
void MyAvatar::destroyAnimGraph() {
|
||||
_rig->destroyAnimGraph();
|
||||
AnimDebugDraw::getInstance().removeSkeleton("myAvatar");
|
||||
AnimDebugDraw::getInstance().removeAnimNode("myAvatar");
|
||||
}
|
||||
|
||||
void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||
|
@ -1261,26 +1259,35 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
|||
initHeadBones();
|
||||
_skeletonModel.setCauterizeBoneSet(_headBoneSet);
|
||||
initAnimGraph();
|
||||
_debugDrawSkeleton = std::make_shared<AnimSkeleton>(_skeletonModel.getGeometry()->getFBXGeometry());
|
||||
}
|
||||
|
||||
if (_enableDebugDrawBindPose || _enableDebugDrawAnimPose) {
|
||||
|
||||
AnimSkeleton::ConstPointer animSkeleton = _rig->getAnimSkeleton();
|
||||
AnimNode::ConstPointer animNode = _rig->getAnimNode();
|
||||
|
||||
// bones space is rotated
|
||||
// bones are aligned such that z is forward, not -z.
|
||||
glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
AnimPose xform(glm::vec3(1), rotY180 * getOrientation(), getPosition());
|
||||
|
||||
if (animSkeleton && _enableDebugDrawBindPose) {
|
||||
if (_enableDebugDrawBindPose && _debugDrawSkeleton) {
|
||||
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
|
||||
AnimDebugDraw::getInstance().addSkeleton("myAvatar", animSkeleton, xform, gray);
|
||||
AnimDebugDraw::getInstance().addSkeleton("myAvatar", _debugDrawSkeleton, xform, gray);
|
||||
}
|
||||
|
||||
// This only works for when new anim system is enabled, at the moment.
|
||||
if (animNode && animSkeleton && _enableDebugDrawAnimPose && _rig->getEnableAnimGraph()) {
|
||||
if (_enableDebugDrawAnimPose && _debugDrawSkeleton) {
|
||||
glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f);
|
||||
AnimDebugDraw::getInstance().addAnimNode("myAvatar", animNode, xform, cyan);
|
||||
|
||||
// build AnimPoseVec from JointStates.
|
||||
AnimPoseVec poses;
|
||||
poses.reserve(_debugDrawSkeleton->getNumJoints());
|
||||
for (int i = 0; i < _debugDrawSkeleton->getNumJoints(); i++) {
|
||||
AnimPose pose = _debugDrawSkeleton->getRelativeBindPose(i);
|
||||
glm::quat jointRot;
|
||||
_rig->getJointRotationInConstrainedFrame(i, jointRot);
|
||||
pose.rot = pose.rot * jointRot;
|
||||
poses.push_back(pose);
|
||||
}
|
||||
|
||||
AnimDebugDraw::getInstance().addPoses("myAvatar", _debugDrawSkeleton, poses, xform, cyan);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -321,6 +321,7 @@ private:
|
|||
|
||||
bool _enableDebugDrawBindPose = false;
|
||||
bool _enableDebugDrawAnimPose = false;
|
||||
AnimSkeleton::ConstPointer _debugDrawSkeleton = nullptr;
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -19,12 +19,8 @@
|
|||
OctreePacketProcessor::OctreePacketProcessor() {
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
|
||||
QSet<PacketType::Value> types {
|
||||
PacketType::OctreeStats, PacketType::EntityData,
|
||||
PacketType::EntityErase, PacketType::OctreeStats
|
||||
};
|
||||
|
||||
packetReceiver.registerDirectListenerForTypes(types, this, "handleOctreePacket");
|
||||
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
}
|
||||
|
||||
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
|
@ -44,7 +40,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, Share
|
|||
Application* app = Application::getInstance();
|
||||
bool wasStatsPacket = false;
|
||||
|
||||
PacketType::Value octreePacketType = packet->getType();
|
||||
PacketType 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
|
||||
|
@ -68,17 +64,17 @@ void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, Share
|
|||
}
|
||||
} // fall through to piggyback message
|
||||
|
||||
PacketType::Value packetType = packet->getType();
|
||||
PacketType packetType = packet->getType();
|
||||
|
||||
// check version of piggyback packet against expected version
|
||||
if (packet->getVersion() != versionForPacketType(packet->getType())) {
|
||||
static QMultiMap<QUuid, PacketType::Value> versionDebugSuppressMap;
|
||||
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
|
||||
|
||||
const QUuid& senderUUID = packet->getSourceID();
|
||||
if (!versionDebugSuppressMap.contains(senderUUID, packetType)) {
|
||||
|
||||
qDebug() << "Packet version mismatch on" << packetType << "- Sender"
|
||||
<< senderUUID << "sent" << (int) packetType << "but"
|
||||
qDebug() << "OctreePacketProcessor - piggyback packet version mismatch on" << packetType << "- Sender"
|
||||
<< senderUUID << "sent" << (int) packet->getVersion() << "but"
|
||||
<< (int) versionForPacketType(packetType) << "expected.";
|
||||
|
||||
emit packetVersionMismatch();
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
class AddressBarDialog : public OffscreenQmlDialog
|
||||
{
|
||||
class AddressBarDialog : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
|
||||
|
|
159
interface/src/ui/AssetUploadDialogFactory.cpp
Normal file
159
interface/src/ui/AssetUploadDialogFactory.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
//
|
||||
// AssetUploadDialogFactory.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 2015-08-26.
|
||||
// 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 "AssetUploadDialogFactory.h"
|
||||
|
||||
#include <AssetClient.h>
|
||||
#include <AssetUpload.h>
|
||||
#include <AssetUtils.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtWidgets/QDialogButtonBox>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() {
|
||||
static AssetUploadDialogFactory staticInstance;
|
||||
return staticInstance;
|
||||
}
|
||||
|
||||
AssetUploadDialogFactory::AssetUploadDialogFactory() {
|
||||
|
||||
}
|
||||
|
||||
static const QString PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server.";
|
||||
|
||||
void AssetUploadDialogFactory::showDialog() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
if (nodeList->getThisNodeCanRez()) {
|
||||
auto filename = QFileDialog::getOpenFileUrl(_dialogParent, "Select a file to upload");
|
||||
|
||||
if (!filename.isEmpty()) {
|
||||
qDebug() << "Selected filename for upload to asset-server: " << filename;
|
||||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
auto upload = assetClient->createUpload(filename.path());
|
||||
|
||||
if (upload) {
|
||||
// connect to the finished signal so we know when the AssetUpload is done
|
||||
QObject::connect(upload, &AssetUpload::finished, this, &AssetUploadDialogFactory::handleUploadFinished);
|
||||
|
||||
// start the upload now
|
||||
upload->start();
|
||||
} else {
|
||||
// show a QMessageBox to say that there is no local asset server
|
||||
QString messageBoxText = QString("Could not upload \n\n%1\n\nbecause you are currently not connected" \
|
||||
" to a local asset-server.").arg(QFileInfo(filename.toString()).fileName());
|
||||
|
||||
QMessageBox::information(_dialogParent, "Failed to Upload", messageBoxText);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// we don't have permission to upload to asset server in this domain - show the permission denied error
|
||||
showErrorDialog(QString(), PERMISSION_DENIED_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const QString& hash) {
|
||||
if (upload->getResult() == AssetUpload::Success) {
|
||||
// show message box for successful upload, with copiable text for ATP hash
|
||||
QDialog* hashCopyDialog = new QDialog(_dialogParent);
|
||||
|
||||
// delete the dialog on close
|
||||
hashCopyDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
// set the window title
|
||||
hashCopyDialog->setWindowTitle(tr("Successful Asset Upload"));
|
||||
|
||||
// setup a layout for the contents of the dialog
|
||||
QVBoxLayout* boxLayout = new QVBoxLayout;
|
||||
|
||||
// set the label text (this shows above the text box)
|
||||
QLabel* lineEditLabel = new QLabel;
|
||||
lineEditLabel->setText(QString("ATP URL for %1").arg(QFileInfo(upload->getFilename()).fileName()));
|
||||
|
||||
// setup the line edit to hold the copiable text
|
||||
QLineEdit* lineEdit = new QLineEdit;
|
||||
|
||||
QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension());
|
||||
|
||||
// set the ATP URL as the text value so it's copiable
|
||||
lineEdit->insert(atpURL);
|
||||
|
||||
// figure out what size this line edit should be using font metrics
|
||||
QFontMetrics textMetrics { lineEdit->font() };
|
||||
|
||||
// set the fixed width on the line edit
|
||||
// pad it by 10 to cover the border and some extra space on the right side (for clicking)
|
||||
static const int LINE_EDIT_RIGHT_PADDING { 10 };
|
||||
|
||||
lineEdit->setFixedWidth(textMetrics.width(atpURL) + LINE_EDIT_RIGHT_PADDING );
|
||||
|
||||
// left align the ATP URL line edit
|
||||
lineEdit->home(true);
|
||||
|
||||
// add the label and line edit to the dialog
|
||||
boxLayout->addWidget(lineEditLabel);
|
||||
boxLayout->addWidget(lineEdit);
|
||||
|
||||
// setup an OK button to close the dialog
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, hashCopyDialog, &QDialog::close);
|
||||
boxLayout->addWidget(buttonBox);
|
||||
|
||||
// set the new layout on the dialog
|
||||
hashCopyDialog->setLayout(boxLayout);
|
||||
|
||||
// show the new dialog
|
||||
hashCopyDialog->show();
|
||||
} else {
|
||||
// figure out the right error message for the message box
|
||||
QString additionalError;
|
||||
|
||||
switch (upload->getResult()) {
|
||||
case AssetUpload::PermissionDenied:
|
||||
additionalError = PERMISSION_DENIED_ERROR;
|
||||
break;
|
||||
case AssetUpload::TooLarge:
|
||||
additionalError = "The uploaded content was too large and could not be stored in the asset-server.";
|
||||
break;
|
||||
case AssetUpload::ErrorLoadingFile:
|
||||
additionalError = "The file could not be opened. Please check your permissions and try again.";
|
||||
break;
|
||||
default:
|
||||
// not handled, do not show a message box
|
||||
return;
|
||||
}
|
||||
|
||||
// display a message box with the error
|
||||
showErrorDialog(QFileInfo(upload->getFilename()).fileName(), additionalError);
|
||||
}
|
||||
|
||||
upload->deleteLater();
|
||||
}
|
||||
|
||||
void AssetUploadDialogFactory::showErrorDialog(const QString& filename, const QString& additionalError) {
|
||||
QString errorMessage;
|
||||
|
||||
if (!filename.isEmpty()) {
|
||||
errorMessage += QString("Failed to upload %1.\n\n").arg(filename);
|
||||
}
|
||||
|
||||
errorMessage += additionalError;
|
||||
|
||||
QMessageBox::warning(_dialogParent, "Failed Upload", errorMessage);
|
||||
}
|
42
interface/src/ui/AssetUploadDialogFactory.h
Normal file
42
interface/src/ui/AssetUploadDialogFactory.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// AssetUploadDialogFactory.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 2015-08-26.
|
||||
// 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef hifi_AssetUploadDialogFactory_h
|
||||
#define hifi_AssetUploadDialogFactory_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class AssetUpload;
|
||||
|
||||
class AssetUploadDialogFactory : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssetUploadDialogFactory(const AssetUploadDialogFactory& other) = delete;
|
||||
AssetUploadDialogFactory& operator=(const AssetUploadDialogFactory& rhs) = delete;
|
||||
|
||||
static AssetUploadDialogFactory& getInstance();
|
||||
|
||||
void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; }
|
||||
public slots:
|
||||
void showDialog();
|
||||
private slots:
|
||||
void handleUploadFinished(AssetUpload* upload, const QString& hash);
|
||||
private:
|
||||
AssetUploadDialogFactory();
|
||||
|
||||
void showErrorDialog(const QString& filename, const QString& additionalError);
|
||||
|
||||
QWidget* _dialogParent { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_AssetUploadDialogFactory_h
|
|
@ -89,10 +89,11 @@ BandwidthDialog::BandwidthDialog(QWidget* parent) :
|
|||
_allChannelDisplays[4] = _otherChannelDisplay =
|
||||
new BandwidthChannelDisplay({NodeType::Unassigned}, form, "Other", "Kbps", 1.0, COLOR2);
|
||||
_allChannelDisplays[5] = _totalChannelDisplay =
|
||||
new BandwidthChannelDisplay({NodeType::DomainServer, NodeType::EntityServer,
|
||||
NodeType::EnvironmentServer, NodeType::AudioMixer, NodeType::Agent,
|
||||
NodeType::AvatarMixer, NodeType::Unassigned},
|
||||
form, "Total", "Kbps", 1.0, COLOR2);
|
||||
new BandwidthChannelDisplay({
|
||||
NodeType::DomainServer, NodeType::EntityServer,
|
||||
NodeType::AudioMixer, NodeType::Agent,
|
||||
NodeType::AvatarMixer, NodeType::Unassigned
|
||||
}, form, "Total", "Kbps", 1.0, COLOR2);
|
||||
|
||||
connect(averageUpdateTimer, SIGNAL(timeout()), this, SLOT(updateTimerTimeout()));
|
||||
averageUpdateTimer->start(1000);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// DialogsManager.cpp
|
||||
//
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Clement on 1/18/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// DialogsManager.h
|
||||
//
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Clement on 1/18/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
class LoginDialog : public OffscreenQmlDialog
|
||||
{
|
||||
class LoginDialog : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
|
||||
|
|
|
@ -126,8 +126,10 @@ void Stats::updateStats() {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||
SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
|
||||
STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
|
||||
STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1);
|
||||
|
||||
//// Now handle entity servers, since there could be more than one, we average their ping times
|
||||
int totalPingOctree = 0;
|
||||
|
|
|
@ -39,6 +39,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, audioPing, 0)
|
||||
STATS_PROPERTY(int, avatarPing, 0)
|
||||
STATS_PROPERTY(int, entitiesPing, 0)
|
||||
STATS_PROPERTY(int, assetPing, 0)
|
||||
STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) )
|
||||
STATS_PROPERTY(float, velocity, 0)
|
||||
STATS_PROPERTY(float, yaw, 0)
|
||||
|
@ -105,6 +106,7 @@ signals:
|
|||
void audioPingChanged();
|
||||
void avatarPingChanged();
|
||||
void entitiesPingChanged();
|
||||
void assetPingChanged();
|
||||
void positionChanged();
|
||||
void velocityChanged();
|
||||
void yawChanged();
|
||||
|
|
|
@ -381,8 +381,8 @@ AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
|
|||
_resource(nullptr) {
|
||||
|
||||
_resource = new Resource(url);
|
||||
connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(onRequestDone(QNetworkReply&)));
|
||||
connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(onRequestError(QNetworkReply::NetworkError)));
|
||||
connect(_resource, &Resource::loaded, this, &AnimNodeLoader::onRequestDone);
|
||||
connect(_resource, &Resource::failed, this, &AnimNodeLoader::onRequestError);
|
||||
}
|
||||
|
||||
AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& jsonUrl) {
|
||||
|
@ -420,8 +420,8 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j
|
|||
return loadNode(rootVal.toObject(), jsonUrl);
|
||||
}
|
||||
|
||||
void AnimNodeLoader::onRequestDone(QNetworkReply& request) {
|
||||
auto node = load(request.readAll(), _url);
|
||||
void AnimNodeLoader::onRequestDone(const QByteArray& data) {
|
||||
auto node = load(data, _url);
|
||||
if (node) {
|
||||
emit success(node);
|
||||
} else {
|
||||
|
|
|
@ -36,7 +36,7 @@ protected:
|
|||
static AnimNode::Pointer load(const QByteArray& contents, const QUrl& jsonUrl);
|
||||
|
||||
protected slots:
|
||||
void onRequestDone(QNetworkReply& request);
|
||||
void onRequestDone(const QByteArray& data);
|
||||
void onRequestError(QNetworkReply::NetworkError error);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -58,7 +58,52 @@ AnimPose::operator glm::mat4() const {
|
|||
glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
|
||||
}
|
||||
|
||||
AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) {
|
||||
// convert to std::vector of joints
|
||||
std::vector<FBXJoint> joints;
|
||||
joints.reserve(fbxGeometry.joints.size());
|
||||
for (auto& joint : fbxGeometry.joints) {
|
||||
joints.push_back(joint);
|
||||
}
|
||||
|
||||
AnimPose geometryOffset(fbxGeometry.offset);
|
||||
buildSkeletonFromJoints(joints, geometryOffset);
|
||||
}
|
||||
|
||||
AnimSkeleton::AnimSkeleton(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset) {
|
||||
buildSkeletonFromJoints(joints, geometryOffset);
|
||||
}
|
||||
|
||||
int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
|
||||
for (size_t i = 0; i < _joints.size(); i++) {
|
||||
if (_joints[i].name == jointName) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AnimSkeleton::getNumJoints() const {
|
||||
return _joints.size();
|
||||
}
|
||||
|
||||
const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
|
||||
return _absoluteBindPoses[jointIndex];
|
||||
}
|
||||
|
||||
const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const {
|
||||
return _relativeBindPoses[jointIndex];
|
||||
}
|
||||
|
||||
int AnimSkeleton::getParentIndex(int jointIndex) const {
|
||||
return _joints[jointIndex].parentIndex;
|
||||
}
|
||||
|
||||
const QString& AnimSkeleton::getJointName(int jointIndex) const {
|
||||
return _joints[jointIndex].name;
|
||||
}
|
||||
|
||||
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset) {
|
||||
_joints = joints;
|
||||
|
||||
// build a cache of bind poses
|
||||
|
@ -113,35 +158,6 @@ AnimSkeleton::AnimSkeleton(const std::vector<FBXJoint>& joints, const AnimPose&
|
|||
}
|
||||
}
|
||||
|
||||
int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
|
||||
for (size_t i = 0; i < _joints.size(); i++) {
|
||||
if (_joints[i].name == jointName) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AnimSkeleton::getNumJoints() const {
|
||||
return _joints.size();
|
||||
}
|
||||
|
||||
AnimPose AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
|
||||
return _absoluteBindPoses[jointIndex];
|
||||
}
|
||||
|
||||
AnimPose AnimSkeleton::getRelativeBindPose(int jointIndex) const {
|
||||
return _relativeBindPoses[jointIndex];
|
||||
}
|
||||
|
||||
int AnimSkeleton::getParentIndex(int jointIndex) const {
|
||||
return _joints[jointIndex].parentIndex;
|
||||
}
|
||||
|
||||
const QString& AnimSkeleton::getJointName(int jointIndex) const {
|
||||
return _joints[jointIndex].name;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void AnimSkeleton::dump() const {
|
||||
qCDebug(animation) << "[";
|
||||
|
|
|
@ -47,16 +47,17 @@ public:
|
|||
using Pointer = std::shared_ptr<AnimSkeleton>;
|
||||
using ConstPointer = std::shared_ptr<const AnimSkeleton>;
|
||||
|
||||
AnimSkeleton(const FBXGeometry& fbxGeometry);
|
||||
AnimSkeleton(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset);
|
||||
int nameToJointIndex(const QString& jointName) const;
|
||||
const QString& getJointName(int jointIndex) const;
|
||||
int getNumJoints() const;
|
||||
|
||||
// absolute pose, not relative to parent
|
||||
AnimPose getAbsoluteBindPose(int jointIndex) const;
|
||||
const AnimPose& getAbsoluteBindPose(int jointIndex) const;
|
||||
|
||||
// relative to parent pose
|
||||
AnimPose getRelativeBindPose(int jointIndex) const;
|
||||
const AnimPose& getRelativeBindPose(int jointIndex) const;
|
||||
|
||||
int getParentIndex(int jointIndex) const;
|
||||
|
||||
|
@ -66,6 +67,8 @@ public:
|
|||
#endif
|
||||
|
||||
protected:
|
||||
void buildSkeletonFromJoints(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset);
|
||||
|
||||
std::vector<FBXJoint> _joints;
|
||||
AnimPoseVec _absoluteBindPoses;
|
||||
AnimPoseVec _relativeBindPoses;
|
||||
|
|
|
@ -39,14 +39,16 @@ QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const Q
|
|||
return QSharedPointer<Resource>(new Animation(url), &Resource::allReferencesCleared);
|
||||
}
|
||||
|
||||
AnimationReader::AnimationReader(const QUrl& url, QNetworkReply* reply) :
|
||||
Animation::Animation(const QUrl& url) : Resource(url) {}
|
||||
|
||||
AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) :
|
||||
_url(url),
|
||||
_reply(reply) {
|
||||
_data(data) {
|
||||
}
|
||||
|
||||
void AnimationReader::run() {
|
||||
try {
|
||||
if (!_reply) {
|
||||
if (_data.isEmpty()) {
|
||||
throw QString("Reply is NULL ?!");
|
||||
}
|
||||
QString urlname = _url.path().toLower();
|
||||
|
@ -58,7 +60,7 @@ void AnimationReader::run() {
|
|||
// Parse the FBX directly from the QNetworkReply
|
||||
FBXGeometry* fbxgeo = nullptr;
|
||||
if (_url.path().toLower().endsWith(".fbx")) {
|
||||
fbxgeo = readFBX(_reply, QVariantHash(), _url.path());
|
||||
fbxgeo = readFBX(_data, QVariantHash(), _url.path());
|
||||
} else {
|
||||
QString errorStr("usupported format");
|
||||
emit onError(299, errorStr);
|
||||
|
@ -71,11 +73,8 @@ void AnimationReader::run() {
|
|||
} catch (const QString& error) {
|
||||
emit onError(299, error);
|
||||
}
|
||||
_reply->deleteLater();
|
||||
}
|
||||
|
||||
Animation::Animation(const QUrl& url) : Resource(url) {}
|
||||
|
||||
bool Animation::isLoaded() const {
|
||||
return _loaded && _geometry;
|
||||
}
|
||||
|
@ -108,9 +107,9 @@ const QVector<FBXAnimationFrame>& Animation::getFramesReference() const {
|
|||
return _geometry->animationFrames;
|
||||
}
|
||||
|
||||
void Animation::downloadFinished(QNetworkReply* reply) {
|
||||
void Animation::downloadFinished(const QByteArray& data) {
|
||||
// parse the animation/fbx file on a background thread.
|
||||
AnimationReader* animationReader = new AnimationReader(reply->url(), reply);
|
||||
AnimationReader* animationReader = new AnimationReader(_url, data);
|
||||
connect(animationReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(animationParseSuccess(FBXGeometry*)));
|
||||
connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString)));
|
||||
QThreadPool::globalInstance()->start(animationReader);
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
const QVector<FBXAnimationFrame>& getFramesReference() const;
|
||||
|
||||
protected:
|
||||
virtual void downloadFinished(QNetworkReply* reply);
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
|
||||
protected slots:
|
||||
void animationParseSuccess(FBXGeometry* geometry);
|
||||
|
@ -81,7 +81,7 @@ class AnimationReader : public QObject, public QRunnable {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AnimationReader(const QUrl& url, QNetworkReply* reply);
|
||||
AnimationReader(const QUrl& url, const QByteArray& data);
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
|
@ -90,7 +90,7 @@ signals:
|
|||
|
||||
private:
|
||||
QUrl _url;
|
||||
QNetworkReply* _reply;
|
||||
QByteArray _data;
|
||||
};
|
||||
|
||||
class AnimationDetails {
|
||||
|
|
|
@ -901,6 +901,14 @@ glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targ
|
|||
return endRotation;
|
||||
}
|
||||
|
||||
bool Rig::getJointRotationInConstrainedFrame(int jointIndex, glm::quat& quatOut) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
quatOut = _jointStates[jointIndex].getRotationInConstrainedFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Rig::updateVisibleJointStates() {
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].slaveVisibleTransform();
|
||||
|
@ -1010,16 +1018,7 @@ void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) {
|
|||
return;
|
||||
}
|
||||
|
||||
// convert to std::vector of joints
|
||||
std::vector<FBXJoint> joints;
|
||||
joints.reserve(fbxGeometry.joints.size());
|
||||
for (auto& joint : fbxGeometry.joints) {
|
||||
joints.push_back(joint);
|
||||
}
|
||||
|
||||
// create skeleton
|
||||
AnimPose geometryOffset(fbxGeometry.offset);
|
||||
_animSkeleton = std::make_shared<AnimSkeleton>(joints, geometryOffset);
|
||||
_animSkeleton = std::make_shared<AnimSkeleton>(fbxGeometry);
|
||||
|
||||
// load the anim graph
|
||||
_animLoader.reset(new AnimNodeLoader(url));
|
||||
|
|
|
@ -50,7 +50,6 @@ class Rig;
|
|||
typedef std::shared_ptr<Rig> RigPointer;
|
||||
|
||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||
|
||||
public:
|
||||
|
||||
struct HeadParameters {
|
||||
|
@ -153,6 +152,7 @@ public:
|
|||
glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex);
|
||||
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
|
||||
float priority, bool constrain = false, float mix = 1.0f);
|
||||
bool getJointRotationInConstrainedFrame(int jointIndex, glm::quat& rotOut) const;
|
||||
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
|
||||
void updateVisibleJointStates();
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ int InboundAudioStream::parseData(NLPacket& packet) {
|
|||
return packet.pos();
|
||||
}
|
||||
|
||||
int InboundAudioStream::parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
||||
int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
||||
if (type == PacketType::SilentAudioFrame) {
|
||||
quint16 numSilentSamples = 0;
|
||||
memcpy(&numSilentSamples, packetAfterSeqNum.constData(), sizeof(quint16));
|
||||
|
@ -177,7 +177,7 @@ int InboundAudioStream::parseStreamProperties(PacketType::Value type, const QByt
|
|||
}
|
||||
}
|
||||
|
||||
int InboundAudioStream::parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) {
|
||||
int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) {
|
||||
return _ringBuffer.writeData(packetAfterStreamProperties.data(), numAudioSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
|
|
|
@ -194,11 +194,11 @@ protected:
|
|||
/// parses the info between the seq num and the audio data in the network packet and calculates
|
||||
/// how many audio samples this packet contains (used when filling in samples for dropped packets).
|
||||
/// default implementation assumes no stream properties and raw audio samples after stream propertiess
|
||||
virtual int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& networkSamples);
|
||||
virtual int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& networkSamples);
|
||||
|
||||
/// parses the audio data in the network packet.
|
||||
/// default implementation assumes packet contains raw audio samples after stream properties
|
||||
virtual int parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int networkSamples);
|
||||
virtual int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int networkSamples);
|
||||
|
||||
/// writes silent samples to the buffer that may be dropped to reduce latency caused by the buffer
|
||||
virtual int writeDroppableSilentSamples(int silentSamples);
|
||||
|
|
|
@ -30,7 +30,7 @@ InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, const bo
|
|||
|
||||
const uchar MAX_INJECTOR_VOLUME = 255;
|
||||
|
||||
int InjectedAudioStream::parseStreamProperties(PacketType::Value type,
|
||||
int InjectedAudioStream::parseStreamProperties(PacketType type,
|
||||
const QByteArray& packetAfterSeqNum,
|
||||
int& numAudioSamples) {
|
||||
// setup a data stream to read from this packet
|
||||
|
|
|
@ -31,7 +31,7 @@ private:
|
|||
InjectedAudioStream& operator= (const InjectedAudioStream&);
|
||||
|
||||
AudioStreamStats getAudioStreamStats() const;
|
||||
int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
|
||||
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
|
||||
|
||||
const QUuid _streamIdentifier;
|
||||
float _radius;
|
||||
|
|
|
@ -42,7 +42,7 @@ int MixedProcessedAudioStream::writeLastFrameRepeatedWithFade(int samples) {
|
|||
return deviceSamplesWritten;
|
||||
}
|
||||
|
||||
int MixedProcessedAudioStream::parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int networkSamples) {
|
||||
int MixedProcessedAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int networkSamples) {
|
||||
|
||||
emit addedStereoSamples(packetAfterStreamProperties);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
protected:
|
||||
int writeDroppableSilentSamples(int silentSamples);
|
||||
int writeLastFrameRepeatedWithFade(int samples);
|
||||
int parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int networkSamples);
|
||||
int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int networkSamples);
|
||||
|
||||
private:
|
||||
int networkToDeviceSamples(int networkSamples);
|
||||
|
|
|
@ -56,16 +56,17 @@ Sound::Sound(const QUrl& url, bool isStereo) :
|
|||
|
||||
}
|
||||
|
||||
void Sound::downloadFinished(QNetworkReply* reply) {
|
||||
void Sound::downloadFinished(const QByteArray& data) {
|
||||
// replace our byte array with the downloaded data
|
||||
QByteArray rawAudioByteArray = reply->readAll();
|
||||
QString fileName = reply->url().fileName();
|
||||
QByteArray rawAudioByteArray = QByteArray(data);
|
||||
QString fileName = getURL().fileName();
|
||||
|
||||
const QString WAV_EXTENSION = ".wav";
|
||||
|
||||
if (reply->hasRawHeader("Content-Type") || fileName.endsWith(WAV_EXTENSION)) {
|
||||
if (fileName.endsWith(WAV_EXTENSION)) {
|
||||
|
||||
QByteArray headerContentType = reply->rawHeader("Content-Type");
|
||||
QString headerContentType = "audio/x-wav";
|
||||
//QByteArray headerContentType = reply->rawHeader("Content-Type");
|
||||
|
||||
// WAV audio file encountered
|
||||
if (headerContentType == "audio/x-wav"
|
||||
|
@ -80,9 +81,9 @@ void Sound::downloadFinished(QNetworkReply* reply) {
|
|||
} else {
|
||||
// check if this was a stereo raw file
|
||||
// since it's raw the only way for us to know that is if the file was called .stereo.raw
|
||||
if (reply->url().fileName().toLower().endsWith("stereo.raw")) {
|
||||
if (fileName.toLower().endsWith("stereo.raw")) {
|
||||
_isStereo = true;
|
||||
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << reply->url() << "as stereo audio file.";
|
||||
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
|
||||
}
|
||||
|
||||
// Process as RAW file
|
||||
|
@ -94,7 +95,6 @@ void Sound::downloadFinished(QNetworkReply* reply) {
|
|||
}
|
||||
|
||||
_isReady = true;
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void Sound::downSample(const QByteArray& rawAudioByteArray) {
|
||||
|
|
|
@ -39,7 +39,7 @@ private:
|
|||
void downSample(const QByteArray& rawAudioByteArray);
|
||||
void interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
|
||||
|
||||
virtual void downloadFinished(QNetworkReply* reply);
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
};
|
||||
|
||||
typedef QSharedPointer<Sound> SharedSoundPointer;
|
||||
|
|
|
@ -154,8 +154,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
_headData->_isFaceTrackerConnected = true;
|
||||
}
|
||||
|
||||
QByteArray avatarDataByteArray;
|
||||
avatarDataByteArray.resize(MAX_PACKET_SIZE);
|
||||
QByteArray avatarDataByteArray(udt::MAX_PACKET_SIZE, 0);
|
||||
|
||||
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(avatarDataByteArray.data());
|
||||
unsigned char* startPosition = destinationBuffer;
|
||||
|
@ -1092,14 +1091,17 @@ void AvatarData::setJointMappingsFromNetworkReply() {
|
|||
|
||||
void AvatarData::sendAvatarDataPacket() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
||||
// about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed.
|
||||
// this is to guard against a joint moving once, the packet getting lost, and the joint never moving again.
|
||||
bool sendFullUpdate = randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO;
|
||||
QByteArray avatarByteArray = toByteArray(true, sendFullUpdate);
|
||||
doneEncoding(true);
|
||||
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
|
||||
static AvatarDataSequenceNumber sequenceNumber = 0;
|
||||
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size() + sizeof(sequenceNumber));
|
||||
avatarPacket->writePrimitive(sequenceNumber++);
|
||||
avatarPacket->write(avatarByteArray);
|
||||
|
||||
nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
|
||||
|
|
|
@ -59,9 +59,11 @@ typedef unsigned long long quint64;
|
|||
#include "Recorder.h"
|
||||
#include "Referential.h"
|
||||
|
||||
typedef std::shared_ptr<AvatarData> AvatarSharedPointer;
|
||||
typedef std::weak_ptr<AvatarData> AvatarWeakPointer;
|
||||
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
|
||||
using AvatarSharedPointer = std::shared_ptr<AvatarData>;
|
||||
using AvatarWeakPointer = std::weak_ptr<AvatarData>;
|
||||
using AvatarHash = QHash<QUuid, AvatarSharedPointer>;
|
||||
|
||||
using AvatarDataSequenceNumber = uint16_t;
|
||||
|
||||
// avatar motion behaviors
|
||||
const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0;
|
||||
|
|
|
@ -43,8 +43,8 @@ public:
|
|||
virtual ~EntityTreeRenderer();
|
||||
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual PacketType::Value getExpectedPacketType() const { return PacketType::EntityData; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; }
|
||||
virtual void renderElement(OctreeElementPointer element, RenderArgs* args);
|
||||
virtual float getSizeScale() const;
|
||||
virtual int getBoundaryLevelAdjust() const;
|
||||
|
|
|
@ -227,7 +227,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
|
||||
if (hasModel()) {
|
||||
if (_model) {
|
||||
if (getModelURL() != _model->getURLAsString()) {
|
||||
if (getModelURL() != _model->getURL().toString()) {
|
||||
qDebug() << "Updating model URL: " << getModelURL();
|
||||
_model->setURL(getModelURL());
|
||||
}
|
||||
|
|
|
@ -28,13 +28,13 @@ void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<NLPacket
|
|||
}
|
||||
}
|
||||
|
||||
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType::Value type, QByteArray& buffer, int clockSkew) {
|
||||
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, int clockSkew) {
|
||||
if (type == PacketType::EntityAdd || type == PacketType::EntityEdit) {
|
||||
EntityItem::adjustEditPacketForClockSkew(buffer, clockSkew);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityEditPacketSender::queueEditEntityMessage(PacketType::Value type, EntityItemID modelID,
|
||||
void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemID modelID,
|
||||
const EntityItemProperties& properties) {
|
||||
if (!_shouldSend) {
|
||||
return; // bail early
|
||||
|
|
|
@ -26,13 +26,13 @@ public:
|
|||
/// 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.
|
||||
/// NOTE: EntityItemProperties assumes that all distances are in meter units
|
||||
void queueEditEntityMessage(PacketType::Value type, EntityItemID modelID, const EntityItemProperties& properties);
|
||||
void queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties);
|
||||
|
||||
void queueEraseEntityMessage(const EntityItemID& entityItemID);
|
||||
|
||||
// My server type is the model server
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual void adjustEditPacketForClockSkew(PacketType::Value type, QByteArray& buffer, int clockSkew);
|
||||
virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, int clockSkew);
|
||||
|
||||
public slots:
|
||||
void processEntityEditNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include <BufferParser.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "RegisteredMetaTypes.h"
|
||||
#include "EntityItemID.h"
|
||||
|
|
|
@ -724,7 +724,7 @@ void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object
|
|||
//
|
||||
// TODO: Implement support for script and visible properties.
|
||||
//
|
||||
bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, EntityItemID id, const EntityItemProperties& properties,
|
||||
bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
||||
QByteArray& buffer) {
|
||||
OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too.
|
||||
OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro
|
||||
|
|
|
@ -197,7 +197,7 @@ public:
|
|||
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
|
||||
void setLocalRenderAlpha(float value) { _localRenderAlpha = value; _localRenderAlphaChanged = true; }
|
||||
|
||||
static bool encodeEntityEditPacket(PacketType::Value command, EntityItemID id, const EntityItemProperties& properties,
|
||||
static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
||||
QByteArray& buffer);
|
||||
|
||||
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);
|
||||
|
|
|
@ -32,7 +32,7 @@ EntityScriptingInterface::EntityScriptingInterface() :
|
|||
connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::queueEntityMessage(PacketType::Value packetType,
|
||||
void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
|
||||
EntityItemID entityID, const EntityItemProperties& properties) {
|
||||
getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties);
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ private:
|
|||
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulation*, EntityItemPointer)> actor);
|
||||
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
||||
void queueEntityMessage(PacketType::Value packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
||||
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
|
||||
|
|
|
@ -71,7 +71,7 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
|||
resetClientEditStats();
|
||||
}
|
||||
|
||||
bool EntityTree::handlesEditPacketType(PacketType::Value packetType) const {
|
||||
bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
||||
// we handle these types of "edit" packets
|
||||
switch (packetType) {
|
||||
case PacketType::EntityAdd:
|
||||
|
|
|
@ -73,10 +73,10 @@ public:
|
|||
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
|
||||
// own definition. Implement these to allow your octree based server to support editing
|
||||
virtual bool getWantSVOfileVersions() const { return true; }
|
||||
virtual PacketType::Value expectedDataPacketType() const { return PacketType::EntityData; }
|
||||
virtual PacketType expectedDataPacketType() const { return PacketType::EntityData; }
|
||||
virtual bool canProcessVersion(PacketVersion thisVersion) const
|
||||
{ return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; }
|
||||
virtual bool handlesEditPacketType(PacketType::Value packetType) const;
|
||||
virtual bool handlesEditPacketType(PacketType packetType) const;
|
||||
virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
|
||||
const SharedNodePointer& senderNode);
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ public:
|
|||
virtual ~EntityTreeHeadlessViewer();
|
||||
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual PacketType::Value getExpectedPacketType() const { return PacketType::EntityData; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; }
|
||||
|
||||
void update();
|
||||
|
||||
|
|
|
@ -194,10 +194,10 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f
|
|||
}
|
||||
|
||||
bool OBJReader::isValidTexture(const QByteArray &filename) {
|
||||
if (!_url) {
|
||||
if (_url.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
QUrl candidateUrl = _url->resolved(QUrl(filename));
|
||||
QUrl candidateUrl = _url.resolved(QUrl(filename));
|
||||
QNetworkReply *netReply = request(candidateUrl, true);
|
||||
bool isValid = netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200);
|
||||
netReply->deleteLater();
|
||||
|
@ -330,7 +330,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
|||
}
|
||||
QByteArray groupName = tokenizer.getDatum();
|
||||
currentGroup = groupName;
|
||||
} else if (token == "mtllib" && _url) {
|
||||
} else if (token == "mtllib" && !_url.isEmpty()) {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
}
|
||||
|
@ -340,7 +340,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
|||
}
|
||||
librariesSeen[libraryName] = true;
|
||||
// Throw away any path part of libraryName, and merge against original url.
|
||||
QUrl libraryUrl = _url->resolved(QUrl(libraryName).fileName());
|
||||
QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName());
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl;
|
||||
#endif
|
||||
|
@ -415,17 +415,14 @@ done:
|
|||
}
|
||||
|
||||
|
||||
FBXGeometry* OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) {
|
||||
QBuffer buffer(const_cast<QByteArray*>(&model));
|
||||
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
|
||||
|
||||
QBuffer buffer { &model };
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
return readOBJ(&buffer, mapping, nullptr);
|
||||
}
|
||||
|
||||
|
||||
FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url) {
|
||||
|
||||
FBXGeometry* geometryPtr = new FBXGeometry();
|
||||
FBXGeometry& geometry = *geometryPtr;
|
||||
OBJTokenizer tokenizer(device);
|
||||
OBJTokenizer tokenizer { &buffer };
|
||||
float scaleGuess = 1.0f;
|
||||
|
||||
_url = url;
|
||||
|
@ -463,8 +460,8 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping,
|
|||
|
||||
// Some .obj files use the convention that a group with uv coordinates that doesn't define a material, should use
|
||||
// a texture with the same basename as the .obj file.
|
||||
if (url) {
|
||||
QString filename = url->fileName();
|
||||
if (!url.isEmpty()) {
|
||||
QString filename = url.fileName();
|
||||
int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail
|
||||
QString basename = filename.remove(extIndex + 1, sizeof("obj"));
|
||||
OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME];
|
||||
|
|
|
@ -71,10 +71,10 @@ public:
|
|||
QHash<QString, OBJMaterial> materials;
|
||||
|
||||
QNetworkReply* request(QUrl& url, bool isTest);
|
||||
FBXGeometry* readOBJ(const QByteArray& model, const QVariantHash& mapping);
|
||||
FBXGeometry* readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url);
|
||||
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url = QUrl());
|
||||
|
||||
private:
|
||||
QUrl* _url = nullptr;
|
||||
QUrl _url;
|
||||
|
||||
QHash<QByteArray, bool> librariesSeen;
|
||||
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess);
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
#include "ShaderCache.h"
|
||||
|
||||
NetworkShader::NetworkShader(const QUrl& url, bool delayLoad)
|
||||
: Resource(url, delayLoad) {};
|
||||
: Resource(url, delayLoad)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetworkShader::downloadFinished(QNetworkReply* reply) {
|
||||
if (reply) {
|
||||
_source = reply->readAll();
|
||||
reply->deleteLater();
|
||||
}
|
||||
void NetworkShader::downloadFinished(const QByteArray& data) {
|
||||
_source = QString::fromUtf8(data);
|
||||
}
|
||||
|
||||
ShaderCache& ShaderCache::instance() {
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
class NetworkShader : public Resource {
|
||||
public:
|
||||
NetworkShader(const QUrl& url, bool delayLoad);
|
||||
virtual void downloadFinished(QNetworkReply* reply) override;
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
|
||||
QByteArray _source;
|
||||
QString _source;
|
||||
};
|
||||
|
||||
using NetworkShaderPointer = QSharedPointer<NetworkShader>;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
//
|
||||
// TextureCache.cpp
|
||||
// libraries/gpu-networking/src
|
||||
//
|
||||
// Created by Andrzej Kapolka on 8/6/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
|
@ -210,8 +213,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArr
|
|||
class ImageReader : public QRunnable {
|
||||
public:
|
||||
|
||||
ImageReader(const QWeakPointer<Resource>& texture, TextureType type, QNetworkReply* reply, const QUrl& url = QUrl(),
|
||||
const QByteArray& content = QByteArray());
|
||||
ImageReader(const QWeakPointer<Resource>& texture, TextureType type, const QByteArray& data, const QUrl& url = QUrl());
|
||||
|
||||
virtual void run();
|
||||
|
||||
|
@ -219,27 +221,27 @@ private:
|
|||
|
||||
QWeakPointer<Resource> _texture;
|
||||
TextureType _type;
|
||||
QNetworkReply* _reply;
|
||||
QUrl _url;
|
||||
QByteArray _content;
|
||||
};
|
||||
|
||||
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
|
||||
void NetworkTexture::downloadFinished(const QByteArray& data) {
|
||||
// send the reader off to the thread pool
|
||||
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, reply));
|
||||
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, data, _url));
|
||||
}
|
||||
|
||||
void NetworkTexture::loadContent(const QByteArray& content) {
|
||||
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, NULL, _url, content));
|
||||
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, content, _url));
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, TextureType type, QNetworkReply* reply,
|
||||
const QUrl& url, const QByteArray& content) :
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, TextureType type, const QByteArray& data,
|
||||
const QUrl& url) :
|
||||
_texture(texture),
|
||||
_type(type),
|
||||
_reply(reply),
|
||||
_url(url),
|
||||
_content(content) {
|
||||
_content(data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::once_flag onceListSupportedFormatsflag;
|
||||
|
@ -292,16 +294,8 @@ public:
|
|||
void ImageReader::run() {
|
||||
QSharedPointer<Resource> texture = _texture.toStrongRef();
|
||||
if (texture.isNull()) {
|
||||
if (_reply) {
|
||||
_reply->deleteLater();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (_reply) {
|
||||
_url = _reply->url();
|
||||
_content = _reply->readAll();
|
||||
_reply->deleteLater();
|
||||
}
|
||||
|
||||
listSupportedImageFormats();
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
//
|
||||
// TextureCache.h
|
||||
// libraries/gpu-networking/src
|
||||
//
|
||||
// Created by Andrzej Kapolka on 8/6/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
|
@ -50,7 +53,7 @@ public:
|
|||
/// Returns the a black texture (useful for a default).
|
||||
const gpu::TexturePointer& getBlackTexture();
|
||||
|
||||
// Returns a map used to compress the normals through a fitting scale algorithm
|
||||
// Returns a map used to compress the normals through a fitting scale algorithm
|
||||
const gpu::TexturePointer& getNormalFittingTexture();
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
|
@ -119,7 +122,7 @@ public:
|
|||
TextureType getType() const { return _type; }
|
||||
protected:
|
||||
|
||||
virtual void downloadFinished(QNetworkReply* reply);
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
|
||||
Q_INVOKABLE void loadContent(const QByteArray& content);
|
||||
// FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on...
|
||||
|
|
|
@ -20,9 +20,10 @@
|
|||
#include <SettingHandle.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "AddressManager.h"
|
||||
#include "NodeList.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "AddressManager.h"
|
||||
|
||||
|
||||
const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
|
||||
const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";
|
||||
|
|
248
libraries/networking/src/AssetClient.cpp
Normal file
248
libraries/networking/src/AssetClient.cpp
Normal file
|
@ -0,0 +1,248 @@
|
|||
//
|
||||
// AssetClient.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Ryan Huffman on 2015/07/21
|
||||
// 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 "AssetClient.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QThread>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AssetRequest.h"
|
||||
#include "AssetUpload.h"
|
||||
#include "NodeList.h"
|
||||
#include "PacketReceiver.h"
|
||||
#include "AssetUtils.h"
|
||||
|
||||
MessageID AssetClient::_currentID = 0;
|
||||
|
||||
|
||||
AssetClient::AssetClient() {
|
||||
|
||||
setCustomDeleter([](Dependency* dependency){
|
||||
static_cast<AssetClient*>(dependency)->deleteLater();
|
||||
});
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfoReply, this, "handleAssetGetInfoReply");
|
||||
packetReceiver.registerMessageListener(PacketType::AssetGetReply, this, "handleAssetGetReply");
|
||||
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
|
||||
}
|
||||
|
||||
AssetRequest* AssetClient::createRequest(const QString& hash, const QString& extension) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
AssetRequest* req;
|
||||
QMetaObject::invokeMethod(this, "createRequest",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(AssetRequest*, req),
|
||||
Q_ARG(QString, hash),
|
||||
Q_ARG(QString, extension));
|
||||
return req;
|
||||
}
|
||||
|
||||
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
|
||||
qDebug() << "Invalid hash size";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
if (!assetServer) {
|
||||
qDebug().nospace() << "Could not request " << hash << "." << extension << " since you are not currently connected to an asset-server.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new AssetRequest(this, hash, extension);
|
||||
}
|
||||
|
||||
AssetUpload* AssetClient::createUpload(const QString& filename) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
AssetUpload* upload;
|
||||
QMetaObject::invokeMethod(this, "createUpload",
|
||||
Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(AssetUpload*, upload),
|
||||
Q_ARG(QString, filename));
|
||||
return upload;
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
if (!assetServer) {
|
||||
qDebug() << "Could not upload" << filename << "since you are not currently connected to an asset-server.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new AssetUpload(this, filename);
|
||||
}
|
||||
|
||||
bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end,
|
||||
ReceivedAssetCallback callback) {
|
||||
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
|
||||
qDebug() << "Invalid hash size";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
if (assetServer) {
|
||||
auto packet = NLPacket::create(PacketType::AssetGet);
|
||||
|
||||
auto messageID = ++_currentID;
|
||||
|
||||
qDebug() << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server.";
|
||||
|
||||
packet->writePrimitive(messageID);
|
||||
|
||||
packet->write(QByteArray::fromHex(hash.toLatin1()));
|
||||
|
||||
packet->writePrimitive(uint8_t(extension.length()));
|
||||
packet->write(extension.toLatin1());
|
||||
|
||||
packet->writePrimitive(start);
|
||||
packet->writePrimitive(end);
|
||||
|
||||
nodeList->sendPacket(std::move(packet), *assetServer);
|
||||
|
||||
_pendingRequests[messageID] = callback;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetClient::getAssetInfo(const QString& hash, const QString& extension, GetInfoCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
if (assetServer) {
|
||||
auto packet = NLPacket::create(PacketType::AssetGetInfo);
|
||||
|
||||
auto messageID = ++_currentID;
|
||||
packet->writePrimitive(messageID);
|
||||
packet->write(QByteArray::fromHex(hash.toLatin1()));
|
||||
packet->writePrimitive(uint8_t(extension.length()));
|
||||
packet->write(extension.toLatin1());
|
||||
|
||||
nodeList->sendPacket(std::move(packet), *assetServer);
|
||||
|
||||
_pendingInfoRequests[messageID] = callback;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssetClient::handleAssetGetInfoReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
MessageID messageID;
|
||||
packet->readPrimitive(&messageID);
|
||||
auto assetHash = packet->read(SHA256_HASH_LENGTH);
|
||||
|
||||
AssetServerError error;
|
||||
packet->readPrimitive(&error);
|
||||
|
||||
AssetInfo info { assetHash.toHex(), 0 };
|
||||
|
||||
if (error == AssetServerError::NoError) {
|
||||
packet->readPrimitive(&info.size);
|
||||
}
|
||||
|
||||
if (_pendingInfoRequests.contains(messageID)) {
|
||||
auto callback = _pendingInfoRequests.take(messageID);
|
||||
callback(error, info);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetClient::handleAssetGetReply(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
QByteArray data = packetList->getMessage();
|
||||
QBuffer packet { &data };
|
||||
packet.open(QIODevice::ReadOnly);
|
||||
|
||||
auto assetHash = packet.read(SHA256_HASH_LENGTH);
|
||||
qDebug() << "Got reply for asset: " << assetHash.toHex();
|
||||
|
||||
MessageID messageID;
|
||||
packet.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
|
||||
|
||||
AssetServerError error;
|
||||
packet.read(reinterpret_cast<char*>(&error), sizeof(AssetServerError));
|
||||
QByteArray assetData;
|
||||
|
||||
if (!error) {
|
||||
DataOffset length;
|
||||
packet.read(reinterpret_cast<char*>(&length), sizeof(DataOffset));
|
||||
data = packet.read(length);
|
||||
} else {
|
||||
qDebug() << "Failure getting asset: " << error;
|
||||
}
|
||||
|
||||
if (_pendingRequests.contains(messageID)) {
|
||||
auto callback = _pendingRequests.take(messageID);
|
||||
callback(error, data);
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetClient::uploadAsset(const QByteArray& data, const QString& extension, UploadResultCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
if (assetServer) {
|
||||
auto packetList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetUpload, QByteArray(), true, true));
|
||||
|
||||
auto messageID = ++_currentID;
|
||||
packetList->writePrimitive(messageID);
|
||||
|
||||
packetList->writePrimitive(static_cast<uint8_t>(extension.length()));
|
||||
packetList->write(extension.toLatin1().constData(), extension.length());
|
||||
|
||||
qDebug() << "Extension length: " << extension.length();
|
||||
qDebug() << "Extension: " << extension;
|
||||
|
||||
uint64_t size = data.length();
|
||||
packetList->writePrimitive(size);
|
||||
packetList->write(data.constData(), size);
|
||||
|
||||
nodeList->sendPacketList(std::move(packetList), *assetServer);
|
||||
|
||||
_pendingUploads[messageID] = callback;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssetClient::handleAssetUploadReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
|
||||
MessageID messageID;
|
||||
packet->readPrimitive(&messageID);
|
||||
|
||||
AssetServerError error;
|
||||
packet->readPrimitive(&error);
|
||||
|
||||
QString hashString { "" };
|
||||
|
||||
if (error) {
|
||||
qDebug() << "Error uploading file to asset server";
|
||||
} else {
|
||||
auto hash = packet->read(SHA256_HASH_LENGTH);
|
||||
hashString = hash.toHex();
|
||||
|
||||
qDebug() << "Successfully uploaded asset to asset-server - SHA256 hash is " << hashString;
|
||||
}
|
||||
|
||||
if (_pendingUploads.contains(messageID)) {
|
||||
auto callback = _pendingUploads.take(messageID);
|
||||
callback(error, hashString);
|
||||
}
|
||||
}
|
63
libraries/networking/src/AssetClient.h
Normal file
63
libraries/networking/src/AssetClient.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// AssetClient.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Ryan Huffman on 2015/07/21
|
||||
// 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_AssetClient_h
|
||||
#define hifi_AssetClient_h
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "AssetUtils.h"
|
||||
#include "LimitedNodeList.h"
|
||||
#include "NLPacket.h"
|
||||
|
||||
class AssetRequest;
|
||||
class AssetUpload;
|
||||
|
||||
struct AssetInfo {
|
||||
QString hash;
|
||||
int64_t size;
|
||||
};
|
||||
|
||||
using ReceivedAssetCallback = std::function<void(AssetServerError error, const QByteArray& data)>;
|
||||
using GetInfoCallback = std::function<void(AssetServerError error, AssetInfo info)>;
|
||||
using UploadResultCallback = std::function<void(AssetServerError error, const QString& hash)>;
|
||||
|
||||
class AssetClient : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssetClient();
|
||||
|
||||
Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension);
|
||||
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
|
||||
|
||||
private slots:
|
||||
void handleAssetGetInfoReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleAssetGetReply(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
void handleAssetUploadReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
|
||||
private:
|
||||
bool getAssetInfo(const QString& hash, const QString& extension, GetInfoCallback callback);
|
||||
bool getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback);
|
||||
bool uploadAsset(const QByteArray& data, const QString& extension, UploadResultCallback callback);
|
||||
|
||||
static MessageID _currentID;
|
||||
QHash<MessageID, ReceivedAssetCallback> _pendingRequests;
|
||||
QHash<MessageID, GetInfoCallback> _pendingInfoRequests;
|
||||
QHash<MessageID, UploadResultCallback> _pendingUploads;
|
||||
|
||||
friend class AssetRequest;
|
||||
friend class AssetUpload;
|
||||
};
|
||||
|
||||
#endif
|
80
libraries/networking/src/AssetRequest.cpp
Normal file
80
libraries/networking/src/AssetRequest.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// AssetRequest.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Ryan Huffman on 2015/07/24
|
||||
// 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 "AssetRequest.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#include "AssetClient.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "NodeList.h"
|
||||
|
||||
AssetRequest::AssetRequest(QObject* parent, const QString& hash, const QString& extension) :
|
||||
QObject(parent),
|
||||
_hash(hash),
|
||||
_extension(extension)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AssetRequest::start() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_state != NOT_STARTED) {
|
||||
qCWarning(networking) << "AssetRequest already started.";
|
||||
return;
|
||||
}
|
||||
|
||||
_state = WAITING_FOR_INFO;
|
||||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->getAssetInfo(_hash, _extension, [this](AssetServerError error, AssetInfo info) {
|
||||
_info = info;
|
||||
_error = error;
|
||||
|
||||
if (_error != NoError) {
|
||||
qCDebug(networking) << "Got error retrieving asset info for" << _hash;
|
||||
_state = FINISHED;
|
||||
emit finished(this);
|
||||
return;
|
||||
}
|
||||
|
||||
_state = WAITING_FOR_DATA;
|
||||
_data.resize(info.size);
|
||||
|
||||
qCDebug(networking) << "Got size of " << _hash << " : " << info.size << " bytes";
|
||||
|
||||
int start = 0, end = _info.size;
|
||||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->getAsset(_hash, _extension, start, end, [this, start, end](AssetServerError error,
|
||||
const QByteArray& data) {
|
||||
Q_ASSERT(data.size() == (end - start));
|
||||
|
||||
_error = error;
|
||||
if (_error == NoError) {
|
||||
memcpy(_data.data() + start, data.constData(), data.size());
|
||||
_totalReceived += data.size();
|
||||
emit progress(_totalReceived, _info.size);
|
||||
} else {
|
||||
qCDebug(networking) << "Got error retrieving asset" << _hash;
|
||||
}
|
||||
|
||||
_state = FINISHED;
|
||||
emit finished(this);
|
||||
});
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue