merge from upstream

This commit is contained in:
Seth Alves 2015-09-08 18:01:07 -07:00
commit 9c4fdfd653
197 changed files with 8515 additions and 1928 deletions

View file

@ -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;

View file

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

View file

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

View file

@ -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:

View file

@ -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");

View file

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

View file

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

View 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);
}
}

View 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

View 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);
}

View 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

View 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);
}

View 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

View file

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

View file

@ -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) {

View file

@ -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) {

View file

@ -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

View file

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

View file

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

View file

@ -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;

View file

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

View file

@ -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

View file

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

View file

@ -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()),

View file

@ -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;

View file

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

View file

@ -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

View file

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

View file

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

View file

@ -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",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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");

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

View file

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

View file

@ -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,

View file

@ -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,

View file

@ -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() {

View file

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

View file

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

View file

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

View file

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

View file

@ -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");

View file

@ -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";

View file

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

View file

@ -321,6 +321,7 @@ private:
bool _enableDebugDrawBindPose = false;
bool _enableDebugDrawAnimPose = false;
AnimSkeleton::ConstPointer _debugDrawSkeleton = nullptr;
};
#endif // hifi_MyAvatar_h

View file

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

View file

@ -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)

View 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);
}

View 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

View file

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

View file

@ -1,6 +1,6 @@
//
// DialogsManager.cpp
//
// interface/src/ui
//
// Created by Clement on 1/18/15.
// Copyright 2015 High Fidelity, Inc.

View file

@ -1,6 +1,6 @@
//
// DialogsManager.h
//
// interface/src/ui
//
// Created by Clement on 1/18/15.
// Copyright 2015 High Fidelity, Inc.

View file

@ -16,8 +16,7 @@
#include <OffscreenQmlDialog.h>
class LoginDialog : public OffscreenQmlDialog
{
class LoginDialog : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL

View file

@ -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;

View file

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

View file

@ -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 {

View file

@ -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:

View file

@ -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) << "[";

View file

@ -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;

View file

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

View file

@ -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 {

View file

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

View file

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

View file

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

View file

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

View file

@ -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

View file

@ -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;

View file

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

View file

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

View file

@ -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) {

View file

@ -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;

View file

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

View file

@ -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;

View file

@ -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;

View file

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

View file

@ -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

View file

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

View file

@ -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"

View file

@ -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

View file

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

View file

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

View file

@ -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

View file

@ -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:

View file

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

View file

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

View file

@ -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];

View file

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

View file

@ -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() {

View file

@ -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>;

View file

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

View file

@ -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...

View file

@ -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";

View 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);
}
}

View 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

View 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