mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 17:00:13 +02:00
Merge pull request #20 from birarda/asset-upload
fix some bugs, add an asset-uploader with copiable URL
This commit is contained in:
commit
db97996319
31 changed files with 519 additions and 69 deletions
|
@ -148,12 +148,32 @@ void AssetServer::handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePoin
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||||
|
|
||||||
auto data = packetList->getMessage();
|
auto data = packetList->getMessage();
|
||||||
QBuffer buffer { &data };
|
QBuffer buffer { &data };
|
||||||
buffer.open(QIODevice::ReadOnly);
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
MessageID messageID;
|
MessageID messageID;
|
||||||
buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
|
buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
|
||||||
|
|
||||||
|
if (!senderNode->getCanRez()) {
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// write the message ID and a permission denied error
|
||||||
|
permissionErrorPacket->writePrimitive(messageID);
|
||||||
|
permissionErrorPacket->writePrimitive(AssetServerError::PERMISSION_DENIED);
|
||||||
|
|
||||||
|
// send off the packet
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode);
|
||||||
|
|
||||||
|
// return so we're not attempting to handle upload
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t extensionLength;
|
uint8_t extensionLength;
|
||||||
buffer.read(reinterpret_cast<char*>(&extensionLength), sizeof(extensionLength));
|
buffer.read(reinterpret_cast<char*>(&extensionLength), sizeof(extensionLength));
|
||||||
|
|
|
@ -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",
|
"name": "audio_env",
|
||||||
"label": "Audio Environment",
|
"label": "Audio Environment",
|
||||||
|
|
|
@ -48,7 +48,8 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||||
<< NodeType::AvatarMixer << NodeType::EntityServer;
|
<< NodeType::AvatarMixer << NodeType::EntityServer
|
||||||
|
<< NodeType::AssetServer;
|
||||||
|
|
||||||
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||||
if (packet->getPayloadSize() == 0) {
|
if (packet->getPayloadSize() == 0) {
|
||||||
|
@ -65,6 +66,16 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
|
||||||
return;
|
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
|
// check if this connect request matches an assignment in the queue
|
||||||
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);
|
||||||
|
|
||||||
|
|
|
@ -285,14 +285,15 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
||||||
// register as the packet receiver for the types we want
|
// register as the packet receiver for the types we want
|
||||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||||
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
||||||
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
|
||||||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||||
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||||
|
|
||||||
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
||||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
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::ICEPing, &_gatekeeper, "processICEPingPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
|
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
|
||||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
|
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
|
||||||
|
@ -571,6 +572,18 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
||||||
&& defaultedType != Assignment::UNUSED_0
|
&& defaultedType != Assignment::UNUSED_0
|
||||||
&& defaultedType != Assignment::UNUSED_1
|
&& defaultedType != Assignment::UNUSED_1
|
||||||
&& defaultedType != Assignment::AgentType) {
|
&& 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
|
// 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
|
// by clearing whatever exists and writing a single default assignment with no payload
|
||||||
Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
|
Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
|
||||||
|
|
|
@ -3763,6 +3763,9 @@ void Application::nodeAdded(SharedNodePointer node) {
|
||||||
if (node->getType() == NodeType::AvatarMixer) {
|
if (node->getType() == NodeType::AvatarMixer) {
|
||||||
// new avatar mixer, send off our identity packet right away
|
// new avatar mixer, send off our identity packet right away
|
||||||
_myAvatar->sendIdentityPacket();
|
_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3810,6 +3813,10 @@ void Application::nodeKilled(SharedNodePointer node) {
|
||||||
} else if (node->getType() == NodeType::AvatarMixer) {
|
} else if (node->getType() == NodeType::AvatarMixer) {
|
||||||
// our avatar mixer has gone away - clear the hash of avatars
|
// our avatar mixer has gone away - clear the hash of avatars
|
||||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,6 @@
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
#include <VrMenu.h>
|
#include <VrMenu.h>
|
||||||
|
|
||||||
#include <AssetClient.h>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "AccountManager.h"
|
#include "AccountManager.h"
|
||||||
#include "audio/AudioScope.h"
|
#include "audio/AudioScope.h"
|
||||||
|
@ -34,13 +32,15 @@
|
||||||
#include "devices/3DConnexionClient.h"
|
#include "devices/3DConnexionClient.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "scripting/MenuScriptingInterface.h"
|
#include "scripting/MenuScriptingInterface.h"
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
#include "ui/AssetUploadDialogFactory.h"
|
||||||
#include "SpeechRecognizer.h"
|
|
||||||
#endif
|
|
||||||
#include "ui/DialogsManager.h"
|
#include "ui/DialogsManager.h"
|
||||||
#include "ui/StandAloneJSConsole.h"
|
#include "ui/StandAloneJSConsole.h"
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||||
|
#include "SpeechRecognizer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
|
||||||
Menu* Menu::_instance = NULL;
|
Menu* Menu::_instance = NULL;
|
||||||
|
@ -90,36 +90,6 @@ Menu::Menu() {
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||||
qApp, SLOT(toggleRunningScriptsWidget()));
|
qApp, SLOT(toggleRunningScriptsWidget()));
|
||||||
|
|
||||||
// Asset uploading
|
|
||||||
{
|
|
||||||
auto action = new QAction("Upload File", fileMenu);
|
|
||||||
fileMenu->addAction(action);
|
|
||||||
action->setMenuRole(QAction::NoRole);
|
|
||||||
_actionHash.insert("Upload File", action);
|
|
||||||
|
|
||||||
connect(action, &QAction::triggered, [this](bool checked) {
|
|
||||||
qDebug() << "Clicked upload file";
|
|
||||||
auto filename = QFileDialog::getOpenFileUrl(nullptr, "Select a file to upload");
|
|
||||||
if (!filename.isEmpty()) {
|
|
||||||
qDebug() << "Selected: " << filename;
|
|
||||||
QFile file { filename.path() };
|
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
QFileInfo fileInfo { filename.path() };
|
|
||||||
auto extension = fileInfo.suffix();
|
|
||||||
auto data = file.readAll();
|
|
||||||
auto assetClient = DependencyManager::get<AssetClient>();
|
|
||||||
assetClient->uploadAsset(data, extension, [this, extension](bool result, QString hash) mutable {
|
|
||||||
if (result) {
|
|
||||||
QMessageBox::information(this, "Upload Successful", "URL: apt:/" + hash + "." + extension);
|
|
||||||
} else {
|
|
||||||
QMessageBox::warning(this, "Upload Failed", "There was an error uploading the file.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto addressManager = DependencyManager::get<AddressManager>();
|
auto addressManager = DependencyManager::get<AddressManager>();
|
||||||
|
|
||||||
addDisabledActionAndSeparator(fileMenu, "History");
|
addDisabledActionAndSeparator(fileMenu, "History");
|
||||||
|
@ -383,7 +353,21 @@ Menu::Menu() {
|
||||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||||
dialogsManager.data(), SLOT(lodTools()));
|
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* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||||
|
|
||||||
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
|
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
|
||||||
|
|
|
@ -284,6 +284,7 @@ namespace MenuOption {
|
||||||
const QString ToolWindow = "Tool Window";
|
const QString ToolWindow = "Tool Window";
|
||||||
const QString TransmitterDrive = "Transmitter Drive";
|
const QString TransmitterDrive = "Transmitter Drive";
|
||||||
const QString TurnWithHead = "Turn using Head";
|
const QString TurnWithHead = "Turn using Head";
|
||||||
|
const QString UploadAsset = "Upload File to Asset Server";
|
||||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||||
const QString UseCamera = "Use Camera";
|
const QString UseCamera = "Use Camera";
|
||||||
const QString VelocityFilter = "Velocity Filter";
|
const QString VelocityFilter = "Velocity Filter";
|
||||||
|
|
|
@ -18,9 +18,8 @@
|
||||||
|
|
||||||
OctreePacketProcessor::OctreePacketProcessor() {
|
OctreePacketProcessor::OctreePacketProcessor() {
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
|
|
||||||
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData,
|
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||||
PacketType::EntityErase, PacketType::OctreeStats },
|
|
||||||
this, "handleOctreePacket");
|
this, "handleOctreePacket");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
|
|
||||||
#include <OffscreenQmlDialog.h>
|
#include <OffscreenQmlDialog.h>
|
||||||
|
|
||||||
class AddressBarDialog : public OffscreenQmlDialog
|
class AddressBarDialog : public OffscreenQmlDialog {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
HIFI_QML_DECL
|
HIFI_QML_DECL
|
||||||
Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
|
Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
|
||||||
|
|
159
interface/src/ui/AssetUploadDialogFactory.cpp
Normal file
159
interface/src/ui/AssetUploadDialogFactory.cpp
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
//
|
||||||
|
// AssetUploadDialogFactory.cpp
|
||||||
|
// interface/src/ui
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2015-08-26.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AssetUploadDialogFactory.h"
|
||||||
|
|
||||||
|
#include <AssetClient.h>
|
||||||
|
#include <AssetUpload.h>
|
||||||
|
#include <AssetUtils.h>
|
||||||
|
#include <NodeList.h>
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtWidgets/QDialogButtonBox>
|
||||||
|
#include <QtWidgets/QFileDialog>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
#include <QtWidgets/QMessageBox>
|
||||||
|
#include <QtWidgets/QLineEdit>
|
||||||
|
#include <QtWidgets/QVBoxLayout>
|
||||||
|
|
||||||
|
AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() {
|
||||||
|
static AssetUploadDialogFactory staticInstance;
|
||||||
|
return staticInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetUploadDialogFactory::AssetUploadDialogFactory() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QString PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server.";
|
||||||
|
|
||||||
|
void AssetUploadDialogFactory::showDialog() {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
if (nodeList->getThisNodeCanRez()) {
|
||||||
|
auto filename = QFileDialog::getOpenFileUrl(_dialogParent, "Select a file to upload");
|
||||||
|
|
||||||
|
if (!filename.isEmpty()) {
|
||||||
|
qDebug() << "Selected filename for upload to asset-server: " << filename;
|
||||||
|
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
auto upload = assetClient->createUpload(filename.path());
|
||||||
|
|
||||||
|
if (upload) {
|
||||||
|
// connect to the finished signal so we know when the AssetUpload is done
|
||||||
|
QObject::connect(upload, &AssetUpload::finished, this, &AssetUploadDialogFactory::handleUploadFinished);
|
||||||
|
|
||||||
|
// start the upload now
|
||||||
|
upload->start();
|
||||||
|
} else {
|
||||||
|
// show a QMessageBox to say that there is no local asset server
|
||||||
|
QString messageBoxText = QString("Could not upload \n\n%1\n\nbecause you are currently not connected" \
|
||||||
|
" to a local asset-server.").arg(QFileInfo(filename.toString()).fileName());
|
||||||
|
|
||||||
|
QMessageBox::information(_dialogParent, "Failed to Upload", messageBoxText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we don't have permission to upload to asset server in this domain - show the permission denied error
|
||||||
|
showErrorDialog(QString(), PERMISSION_DENIED_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const QString& hash) {
|
||||||
|
if (upload->getResult() == AssetUpload::Success) {
|
||||||
|
// show message box for successful upload, with copiable text for ATP hash
|
||||||
|
QDialog* hashCopyDialog = new QDialog(_dialogParent);
|
||||||
|
|
||||||
|
// delete the dialog on close
|
||||||
|
hashCopyDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
|
// set the window title
|
||||||
|
hashCopyDialog->setWindowTitle(tr("Successful Asset Upload"));
|
||||||
|
|
||||||
|
// setup a layout for the contents of the dialog
|
||||||
|
QVBoxLayout* boxLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
// set the label text (this shows above the text box)
|
||||||
|
QLabel* lineEditLabel = new QLabel;
|
||||||
|
lineEditLabel->setText(QString("ATP URL for %1").arg(QFileInfo(upload->getFilename()).fileName()));
|
||||||
|
|
||||||
|
// setup the line edit to hold the copiable text
|
||||||
|
QLineEdit* lineEdit = new QLineEdit;
|
||||||
|
|
||||||
|
QString atpURL = QString("%1://%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension());
|
||||||
|
|
||||||
|
// set the ATP URL as the text value so it's copiable
|
||||||
|
lineEdit->insert(atpURL);
|
||||||
|
|
||||||
|
// figure out what size this line edit should be using font metrics
|
||||||
|
QFontMetrics textMetrics { lineEdit->font() };
|
||||||
|
|
||||||
|
// set the fixed width on the line edit
|
||||||
|
// pad it by 10 to cover the border and some extra space on the right side (for clicking)
|
||||||
|
static const int LINE_EDIT_RIGHT_PADDING { 10 };
|
||||||
|
|
||||||
|
lineEdit->setFixedWidth(textMetrics.width(atpURL) + LINE_EDIT_RIGHT_PADDING );
|
||||||
|
|
||||||
|
// left align the ATP URL line edit
|
||||||
|
lineEdit->home(true);
|
||||||
|
|
||||||
|
// add the label and line edit to the dialog
|
||||||
|
boxLayout->addWidget(lineEditLabel);
|
||||||
|
boxLayout->addWidget(lineEdit);
|
||||||
|
|
||||||
|
// setup an OK button to close the dialog
|
||||||
|
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||||
|
connect(buttonBox, &QDialogButtonBox::accepted, hashCopyDialog, &QDialog::close);
|
||||||
|
boxLayout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
// set the new layout on the dialog
|
||||||
|
hashCopyDialog->setLayout(boxLayout);
|
||||||
|
|
||||||
|
// show the new dialog
|
||||||
|
hashCopyDialog->show();
|
||||||
|
} else {
|
||||||
|
// figure out the right error message for the message box
|
||||||
|
QString additionalError;
|
||||||
|
|
||||||
|
switch (upload->getResult()) {
|
||||||
|
case AssetUpload::PermissionDenied:
|
||||||
|
additionalError = PERMISSION_DENIED_ERROR;
|
||||||
|
break;
|
||||||
|
case AssetUpload::TooLarge:
|
||||||
|
additionalError = "The uploaded content was too large and could not be stored in the asset-server.";
|
||||||
|
break;
|
||||||
|
case AssetUpload::ErrorLoadingFile:
|
||||||
|
additionalError = "The file could not be opened. Please check your permissions and try again.";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// not handled, do not show a message box
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// display a message box with the error
|
||||||
|
showErrorDialog(QFileInfo(upload->getFilename()).fileName(), additionalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
upload->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetUploadDialogFactory::showErrorDialog(const QString& filename, const QString& additionalError) {
|
||||||
|
QString errorMessage;
|
||||||
|
|
||||||
|
if (!filename.isEmpty()) {
|
||||||
|
errorMessage += QString("Failed to upload %1.\n\n").arg(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessage += additionalError;
|
||||||
|
|
||||||
|
QMessageBox::warning(_dialogParent, "Failed Upload", errorMessage);
|
||||||
|
}
|
42
interface/src/ui/AssetUploadDialogFactory.h
Normal file
42
interface/src/ui/AssetUploadDialogFactory.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// AssetUploadDialogFactory.h
|
||||||
|
// interface/src/ui
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2015-08-26.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_AssetUploadDialogFactory_h
|
||||||
|
#define hifi_AssetUploadDialogFactory_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
class AssetUpload;
|
||||||
|
|
||||||
|
class AssetUploadDialogFactory : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AssetUploadDialogFactory(const AssetUploadDialogFactory& other) = delete;
|
||||||
|
AssetUploadDialogFactory& operator=(const AssetUploadDialogFactory& rhs) = delete;
|
||||||
|
|
||||||
|
static AssetUploadDialogFactory& getInstance();
|
||||||
|
|
||||||
|
void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; }
|
||||||
|
public slots:
|
||||||
|
void showDialog();
|
||||||
|
private slots:
|
||||||
|
void handleUploadFinished(AssetUpload* upload, const QString& hash);
|
||||||
|
private:
|
||||||
|
AssetUploadDialogFactory();
|
||||||
|
|
||||||
|
void showErrorDialog(const QString& filename, const QString& additionalError);
|
||||||
|
|
||||||
|
QWidget* _dialogParent { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AssetUploadDialogFactory_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// DialogsManager.cpp
|
// DialogsManager.cpp
|
||||||
//
|
// interface/src/ui
|
||||||
//
|
//
|
||||||
// Created by Clement on 1/18/15.
|
// Created by Clement on 1/18/15.
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// DialogsManager.h
|
// DialogsManager.h
|
||||||
//
|
// interface/src/ui
|
||||||
//
|
//
|
||||||
// Created by Clement on 1/18/15.
|
// Created by Clement on 1/18/15.
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
|
|
||||||
#include <OffscreenQmlDialog.h>
|
#include <OffscreenQmlDialog.h>
|
||||||
|
|
||||||
class LoginDialog : public OffscreenQmlDialog
|
class LoginDialog : public OffscreenQmlDialog {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
HIFI_QML_DECL
|
HIFI_QML_DECL
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#include "AssetRequest.h"
|
#include "AssetRequest.h"
|
||||||
|
#include "AssetUpload.h"
|
||||||
#include "NodeList.h"
|
#include "NodeList.h"
|
||||||
#include "PacketReceiver.h"
|
#include "PacketReceiver.h"
|
||||||
#include "AssetUtils.h"
|
#include "AssetUtils.h"
|
||||||
|
@ -29,10 +30,10 @@ AssetClient::AssetClient() {
|
||||||
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
|
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetRequest* AssetClient::create(QString hash, QString extension) {
|
AssetRequest* AssetClient::createRequest(QString hash, QString extension) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
AssetRequest* req;
|
AssetRequest* req;
|
||||||
QMetaObject::invokeMethod(this, "create",
|
QMetaObject::invokeMethod(this, "createRequest",
|
||||||
Qt::BlockingQueuedConnection,
|
Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(AssetRequest*, req),
|
Q_RETURN_ARG(AssetRequest*, req),
|
||||||
Q_ARG(QString, hash),
|
Q_ARG(QString, hash),
|
||||||
|
@ -49,15 +50,32 @@ AssetRequest* AssetClient::create(QString hash, QString extension) {
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
auto assetClient = DependencyManager::get<AssetClient>();
|
return new AssetRequest(this, hash, extension);
|
||||||
auto request = new AssetRequest(assetClient.data(), hash, extension);
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AssetUpload* AssetClient::createUpload(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) {
|
||||||
|
return new AssetUpload(this, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool AssetClient::getAsset(QString hash, QString extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback) {
|
bool AssetClient::getAsset(QString hash, QString extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback) {
|
||||||
if (hash.length() != HASH_HEX_LENGTH) {
|
if (hash.length() != HASH_HEX_LENGTH) {
|
||||||
qDebug() << "Invalid hash size";
|
qDebug() << "Invalid hash size";
|
||||||
|
@ -71,6 +89,9 @@ bool AssetClient::getAsset(QString hash, QString extension, DataOffset start, Da
|
||||||
auto packet = NLPacket::create(PacketType::AssetGet);
|
auto packet = NLPacket::create(PacketType::AssetGet);
|
||||||
|
|
||||||
auto messageID = ++_currentID;
|
auto messageID = ++_currentID;
|
||||||
|
|
||||||
|
qDebug() << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server.";
|
||||||
|
|
||||||
packet->writePrimitive(messageID);
|
packet->writePrimitive(messageID);
|
||||||
|
|
||||||
packet->write(hash.toLatin1());
|
packet->write(hash.toLatin1());
|
||||||
|
@ -166,6 +187,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer<NLPacketList> packetList, S
|
||||||
bool AssetClient::uploadAsset(QByteArray data, QString extension, UploadResultCallback callback) {
|
bool AssetClient::uploadAsset(QByteArray data, QString extension, UploadResultCallback callback) {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
|
|
||||||
if (assetServer) {
|
if (assetServer) {
|
||||||
auto packetList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetUpload, QByteArray(), true, true));
|
auto packetList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetUpload, QByteArray(), true, true));
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "NLPacket.h"
|
#include "NLPacket.h"
|
||||||
|
|
||||||
class AssetRequest;
|
class AssetRequest;
|
||||||
|
class AssetUpload;
|
||||||
|
|
||||||
struct AssetInfo {
|
struct AssetInfo {
|
||||||
QString hash;
|
QString hash;
|
||||||
|
@ -37,7 +38,8 @@ class AssetClient : public QObject, public Dependency {
|
||||||
public:
|
public:
|
||||||
AssetClient();
|
AssetClient();
|
||||||
|
|
||||||
Q_INVOKABLE AssetRequest* create(QString hash, QString extension);
|
Q_INVOKABLE AssetRequest* createRequest(QString hash, QString extension);
|
||||||
|
Q_INVOKABLE AssetUpload* createUpload(QString filename);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleAssetGetInfoReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
void handleAssetGetInfoReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||||
|
@ -45,9 +47,6 @@ private slots:
|
||||||
void handleAssetUploadReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
void handleAssetUploadReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class AssetRequest;
|
|
||||||
friend class Menu;
|
|
||||||
|
|
||||||
bool getAssetInfo(QString hash, QString extension, GetInfoCallback callback);
|
bool getAssetInfo(QString hash, QString extension, GetInfoCallback callback);
|
||||||
bool getAsset(QString hash, QString extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback);
|
bool getAsset(QString hash, QString extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback);
|
||||||
bool uploadAsset(QByteArray data, QString extension, UploadResultCallback callback);
|
bool uploadAsset(QByteArray data, QString extension, UploadResultCallback callback);
|
||||||
|
@ -56,6 +55,9 @@ private:
|
||||||
QHash<MessageID, ReceivedAssetCallback> _pendingRequests;
|
QHash<MessageID, ReceivedAssetCallback> _pendingRequests;
|
||||||
QHash<MessageID, GetInfoCallback> _pendingInfoRequests;
|
QHash<MessageID, GetInfoCallback> _pendingInfoRequests;
|
||||||
QHash<MessageID, UploadResultCallback> _pendingUploads;
|
QHash<MessageID, UploadResultCallback> _pendingUploads;
|
||||||
|
|
||||||
|
friend class AssetRequest;
|
||||||
|
friend class AssetUpload;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -50,6 +50,7 @@ void AssetRequest::start() {
|
||||||
++_numPendingRequests;
|
++_numPendingRequests;
|
||||||
auto start = i * CHUNK_SIZE;
|
auto start = i * CHUNK_SIZE;
|
||||||
auto end = std::min((i + 1) * CHUNK_SIZE, info.size);
|
auto end = std::min((i + 1) * CHUNK_SIZE, info.size);
|
||||||
|
|
||||||
assetClient->getAsset(_hash, _extension, start, end, [this, start, end](bool success, QByteArray data) {
|
assetClient->getAsset(_hash, _extension, start, end, [this, start, end](bool success, QByteArray data) {
|
||||||
Q_ASSERT(data.size() == (end - start));
|
Q_ASSERT(data.size() == (end - start));
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ void AssetResourceRequest::doSend() {
|
||||||
auto hash = parts[0];
|
auto hash = parts[0];
|
||||||
auto extension = parts.length() > 1 ? parts[1] : "";
|
auto extension = parts.length() > 1 ? parts[1] : "";
|
||||||
|
|
||||||
auto request = assetClient->create(hash, extension);
|
auto request = assetClient->createRequest(hash, extension);
|
||||||
|
|
||||||
if (!request) {
|
if (!request) {
|
||||||
return;
|
return;
|
||||||
|
|
67
libraries/networking/src/AssetUpload.cpp
Normal file
67
libraries/networking/src/AssetUpload.cpp
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
//
|
||||||
|
// AssetUpload.cpp
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// 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 "AssetUpload.h"
|
||||||
|
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include "AssetClient.h"
|
||||||
|
|
||||||
|
AssetUpload::AssetUpload(QObject* object, const QString& filename) :
|
||||||
|
_filename(filename)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetUpload::start() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to open the file at the given filename
|
||||||
|
QFile file { _filename };
|
||||||
|
|
||||||
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
|
|
||||||
|
// file opened, read the data and grab the extension
|
||||||
|
_extension = QFileInfo(_filename).suffix();
|
||||||
|
|
||||||
|
auto data = file.readAll();
|
||||||
|
|
||||||
|
// ask the AssetClient to upload the asset and emit the proper signals from the passed callback
|
||||||
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
|
|
||||||
|
assetClient->uploadAsset(data, _extension, [this](bool success, QString hash){
|
||||||
|
if (success) {
|
||||||
|
// successful upload - emit finished with a point to ourselves and the resulting hash
|
||||||
|
_result = Success;
|
||||||
|
|
||||||
|
emit finished(this, hash);
|
||||||
|
} else {
|
||||||
|
// error during upload - emit finished with an empty hash
|
||||||
|
// callers can get the error from this object
|
||||||
|
|
||||||
|
// TODO: get the actual error from the callback
|
||||||
|
_result = PermissionDenied;
|
||||||
|
|
||||||
|
emit finished(this, hash);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// we couldn't open the file - set the error result
|
||||||
|
_result = ErrorLoadingFile;
|
||||||
|
|
||||||
|
// emit that we are done
|
||||||
|
emit finished(this, QString());
|
||||||
|
}
|
||||||
|
}
|
53
libraries/networking/src/AssetUpload.h
Normal file
53
libraries/networking/src/AssetUpload.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// AssetUpload.h
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// 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_AssetUpload_h
|
||||||
|
#define hifi_AssetUpload_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
// You should be able to upload an asset from any thread, and handle the responses in a safe way
|
||||||
|
// on your own thread. Everything should happen on AssetClient's thread, the caller should
|
||||||
|
// receive events by connecting to signals on an object that lives on AssetClient's threads.
|
||||||
|
|
||||||
|
class AssetUpload : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum Result {
|
||||||
|
Success = 0,
|
||||||
|
Timeout,
|
||||||
|
TooLarge,
|
||||||
|
PermissionDenied,
|
||||||
|
ErrorLoadingFile
|
||||||
|
};
|
||||||
|
|
||||||
|
AssetUpload(QObject* parent, const QString& filename);
|
||||||
|
|
||||||
|
Q_INVOKABLE void start();
|
||||||
|
|
||||||
|
const QString& getFilename() const { return _filename; }
|
||||||
|
const QString& getExtension() const { return _extension; }
|
||||||
|
const Result& getResult() const { return _result; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished(AssetUpload* upload, const QString& hash);
|
||||||
|
void progress(uint64_t totalReceived, uint64_t total);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _filename;
|
||||||
|
QString _extension;
|
||||||
|
Result _result;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AssetUpload_h
|
|
@ -25,6 +25,9 @@ enum AssetServerError : uint8_t {
|
||||||
ASSET_NOT_FOUND,
|
ASSET_NOT_FOUND,
|
||||||
INVALID_BYTE_RANGE,
|
INVALID_BYTE_RANGE,
|
||||||
ASSET_TOO_LARGE,
|
ASSET_TOO_LARGE,
|
||||||
|
PERMISSION_DENIED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const QString ATP_SCHEME = "atp";
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -127,6 +127,8 @@ const char* Assignment::getTypeName() const {
|
||||||
return "avatar-mixer";
|
return "avatar-mixer";
|
||||||
case Assignment::AgentType:
|
case Assignment::AgentType:
|
||||||
return "agent";
|
return "agent";
|
||||||
|
case Assignment::AssetServerType:
|
||||||
|
return "asset-server";
|
||||||
case Assignment::EntityServerType:
|
case Assignment::EntityServerType:
|
||||||
return "entity-server";
|
return "entity-server";
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -409,6 +409,9 @@ void LimitedNodeList::eraseAllNodes() {
|
||||||
|
|
||||||
void LimitedNodeList::reset() {
|
void LimitedNodeList::reset() {
|
||||||
eraseAllNodes();
|
eraseAllNodes();
|
||||||
|
|
||||||
|
// we need to make sure any socket connections are gone so wait on that here
|
||||||
|
_nodeSocket.clearConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ void NodeType::init() {
|
||||||
TypeNameHash.insert(NodeType::Agent, "Agent");
|
TypeNameHash.insert(NodeType::Agent, "Agent");
|
||||||
TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer");
|
TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer");
|
||||||
TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
|
TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
|
||||||
|
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
||||||
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ void PacketReceiver::registerVerifiedListener(PacketType type, QObject* object,
|
||||||
qCWarning(networking) << "Registering a packet listener for packet type" << type
|
qCWarning(networking) << "Registering a packet listener for packet type" << type
|
||||||
<< "that will remove a previously registered listener";
|
<< "that will remove a previously registered listener";
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the mapping
|
// add the mapping
|
||||||
_packetListenerMap[type] = ObjectMethodPair(QPointer<QObject>(object), slot);
|
_packetListenerMap[type] = ObjectMethodPair(QPointer<QObject>(object), slot);
|
||||||
}
|
}
|
||||||
|
@ -210,10 +210,30 @@ void PacketReceiver::unregisterListener(QObject* listener) {
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker packetListenerLocker(&_packetListenerLock);
|
QMutexLocker packetListenerLocker(&_packetListenerLock);
|
||||||
std::remove_if(std::begin(_packetListenerMap), std::end(_packetListenerMap),
|
|
||||||
[&listener](const ObjectMethodPair& pair) {
|
// TODO: replace the two while loops below with a replace_if on the vector (once we move to Message everywhere)
|
||||||
return pair.first == listener;
|
|
||||||
});
|
// clear any registrations for this listener in _packetListenerMap
|
||||||
|
auto it = _packetListenerMap.begin();
|
||||||
|
|
||||||
|
while (it != _packetListenerMap.end()) {
|
||||||
|
if (it.value().first == listener) {
|
||||||
|
it = _packetListenerMap.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear any registrations for this listener in _packetListListener
|
||||||
|
auto listIt = _packetListListenerMap.begin();
|
||||||
|
|
||||||
|
while (listIt != _packetListListenerMap.end()) {
|
||||||
|
if (listIt.value().first == listener) {
|
||||||
|
listIt = _packetListListenerMap.erase(listIt);
|
||||||
|
} else {
|
||||||
|
++listIt;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker directConnectSetLocker(&_directConnectSetMutex);
|
QMutexLocker directConnectSetLocker(&_directConnectSetMutex);
|
||||||
|
|
|
@ -69,6 +69,7 @@ private:
|
||||||
using ObjectMethodPair = std::pair<QPointer<QObject>, QMetaMethod>;
|
using ObjectMethodPair = std::pair<QPointer<QObject>, QMetaMethod>;
|
||||||
|
|
||||||
QMutex _packetListenerLock;
|
QMutex _packetListenerLock;
|
||||||
|
// TODO: replace the two following hashes with an std::vector once we switch Packet/PacketList to Message
|
||||||
QHash<PacketType, ObjectMethodPair> _packetListenerMap;
|
QHash<PacketType, ObjectMethodPair> _packetListenerMap;
|
||||||
QHash<PacketType, ObjectMethodPair> _packetListListenerMap;
|
QHash<PacketType, ObjectMethodPair> _packetListListenerMap;
|
||||||
int _inPacketCount = 0;
|
int _inPacketCount = 0;
|
||||||
|
|
|
@ -659,6 +659,7 @@ void Connection::processTimeoutNAK(std::unique_ptr<ControlPacket> controlPacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::resetReceiveState() {
|
void Connection::resetReceiveState() {
|
||||||
|
|
||||||
// reset all SequenceNumber member variables back to default
|
// reset all SequenceNumber member variables back to default
|
||||||
SequenceNumber defaultSequenceNumber;
|
SequenceNumber defaultSequenceNumber;
|
||||||
|
|
||||||
|
@ -669,6 +670,9 @@ void Connection::resetReceiveState() {
|
||||||
|
|
||||||
_lastSentACK = defaultSequenceNumber;
|
_lastSentACK = defaultSequenceNumber;
|
||||||
|
|
||||||
|
// clear the sent ACKs
|
||||||
|
_sentACKs.clear();
|
||||||
|
|
||||||
// clear the loss list and _lastNAKTime
|
// clear the loss list and _lastNAKTime
|
||||||
_lossList.clear();
|
_lossList.clear();
|
||||||
_lastNAKTime = high_resolution_clock::time_point();
|
_lastNAKTime = high_resolution_clock::time_point();
|
||||||
|
|
|
@ -134,6 +134,12 @@ void SendQueue::queuePacketList(std::unique_ptr<PacketList> packetList) {
|
||||||
|
|
||||||
void SendQueue::stop() {
|
void SendQueue::stop() {
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
|
|
||||||
|
// in case we're waiting to send another handshake, release the condition_variable now so we cleanup sooner
|
||||||
|
_handshakeACKCondition.notify_one();
|
||||||
|
|
||||||
|
// in case the empty condition is waiting for packets/loss release it now so that the queue is cleaned up
|
||||||
|
_emptyCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::sendPacket(const Packet& packet) {
|
void SendQueue::sendPacket(const Packet& packet) {
|
||||||
|
@ -274,7 +280,7 @@ void SendQueue::run() {
|
||||||
|
|
||||||
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
|
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
|
||||||
// (this is according to the current flow window size) then we send out a new packet
|
// (this is according to the current flow window size) then we send out a new packet
|
||||||
if (!sentAPacket) {
|
if (_hasReceivedHandshakeACK && !sentAPacket) {
|
||||||
flowWindowFull = (seqlen(SequenceNumber { (uint32_t) _lastACKSequenceNumber }, _currentSequenceNumber) >
|
flowWindowFull = (seqlen(SequenceNumber { (uint32_t) _lastACKSequenceNumber }, _currentSequenceNumber) >
|
||||||
_flowWindowSize);
|
_flowWindowSize);
|
||||||
sentAPacket = maybeSendNewPacket();
|
sentAPacket = maybeSendNewPacket();
|
||||||
|
@ -299,8 +305,9 @@ void SendQueue::run() {
|
||||||
|
|
||||||
if (flowWindowFull && (high_resolution_clock::now() - _flowWindowFullSince) > CONSIDER_INACTIVE_AFTER) {
|
if (flowWindowFull && (high_resolution_clock::now() - _flowWindowFullSince) > CONSIDER_INACTIVE_AFTER) {
|
||||||
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
||||||
// then signal the queue is inactive
|
// then signal the queue is inactive and return so it can be cleaned up
|
||||||
emit queueInactive();
|
emit queueInactive();
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
// During our processing above we didn't send any packets and the flow window is not full.
|
// During our processing above we didn't send any packets and the flow window is not full.
|
||||||
|
|
||||||
|
@ -310,15 +317,18 @@ void SendQueue::run() {
|
||||||
|
|
||||||
// The packets queue and loss list mutexes are now both locked - check if they're still both empty
|
// The packets queue and loss list mutexes are now both locked - check if they're still both empty
|
||||||
if (doubleLock.try_lock() && _packets.empty() && _naks.getLength() == 0) {
|
if (doubleLock.try_lock() && _packets.empty() && _naks.getLength() == 0) {
|
||||||
|
|
||||||
// both are empty - let's use a condition_variable_any to wait
|
// both are empty - let's use a condition_variable_any to wait
|
||||||
auto cvStatus = _emptyCondition.wait_for(doubleLock, CONSIDER_INACTIVE_AFTER);
|
auto cvStatus = _emptyCondition.wait_for(doubleLock, CONSIDER_INACTIVE_AFTER);
|
||||||
|
|
||||||
// we have the double lock again - Make sure to unlock it
|
// we have the double lock again - Make sure to unlock it
|
||||||
doubleLock.unlock();
|
doubleLock.unlock();
|
||||||
|
|
||||||
// Check if we've been inactive for too long
|
|
||||||
if (cvStatus == std::cv_status::timeout) {
|
if (cvStatus == std::cv_status::timeout) {
|
||||||
|
// the wait_for released because we've been inactive for too long
|
||||||
|
// so emit our inactive signal and return so the send queue can be cleaned up
|
||||||
emit queueInactive();
|
emit queueInactive();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip to the next iteration
|
// skip to the next iteration
|
||||||
|
|
|
@ -83,7 +83,7 @@ private:
|
||||||
void sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber);
|
void sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber);
|
||||||
|
|
||||||
bool maybeSendNewPacket(); // Figures out what packet to send next
|
bool maybeSendNewPacket(); // Figures out what packet to send next
|
||||||
bool maybeResendPacket(); // Determines whether to resend a packet and wich one
|
bool maybeResendPacket(); // Determines whether to resend a packet and which one
|
||||||
|
|
||||||
// Increments current sequence number and return it
|
// Increments current sequence number and return it
|
||||||
SequenceNumber getNextSequenceNumber();
|
SequenceNumber getNextSequenceNumber();
|
||||||
|
|
|
@ -154,8 +154,19 @@ Connection& Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
|
||||||
return *it->second;
|
return *it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Socket::clearConnections() {
|
||||||
|
if (thread() != QThread::currentThread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "clearConnections", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear all of the current connections in the socket
|
||||||
|
qDebug() << "Clearing all remaining connections in Socket.";
|
||||||
|
_connectionsHash.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void Socket::cleanupConnection(HifiSockAddr sockAddr) {
|
void Socket::cleanupConnection(HifiSockAddr sockAddr) {
|
||||||
qCDebug(networking) << "Socket::cleanupConnection called for connection to" << sockAddr;
|
qCDebug(networking) << "Socket::cleanupConnection called for UDT connection to" << sockAddr;
|
||||||
_connectionsHash.erase(sockAddr);
|
_connectionsHash.erase(sockAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void cleanupConnection(HifiSockAddr sockAddr);
|
void cleanupConnection(HifiSockAddr sockAddr);
|
||||||
|
void clearConnections();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void readPendingDatagrams();
|
void readPendingDatagrams();
|
||||||
|
|
Loading…
Reference in a new issue