Merge branch 'atp' of github.com:birarda/hifi into atp

This commit is contained in:
Stephen Birarda 2016-03-09 12:16:59 -08:00
commit 2e9ee4f8a7
14 changed files with 743 additions and 456 deletions

View file

@ -21,11 +21,12 @@
#include <QtCore/QJsonDocument> #include <QtCore/QJsonDocument>
#include <QtCore/QString> #include <QtCore/QString>
#include <ServerPathUtils.h>
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "NodeType.h" #include "NodeType.h"
#include "SendAssetTask.h" #include "SendAssetTask.h"
#include "UploadAssetTask.h" #include "UploadAssetTask.h"
#include <ServerPathUtils.h>
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
@ -203,6 +204,10 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer<ReceivedMessage> me
handleDeleteMappingsOperation(*message, senderNode, *replyPacket); handleDeleteMappingsOperation(*message, senderNode, *replyPacket);
break; break;
} }
case AssetMappingOperationType::Rename: {
handleRenameMappingOperation(*message, senderNode, *replyPacket);
break;
}
} }
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
@ -274,6 +279,21 @@ void AssetServer::handleDeleteMappingsOperation(ReceivedMessage& message, Shared
} }
} }
void AssetServer::handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
if (senderNode->getCanRez()) {
QString oldPath = message.readString();
QString newPath = message.readString();
if (renameMapping(oldPath, newPath)) {
replyPacket.writePrimitive(AssetServerError::NoError);
} else {
replyPacket.writePrimitive(AssetServerError::MappingOperationFailed);
}
} else {
replyPacket.writePrimitive(AssetServerError::PermissionDenied);
}
}
void AssetServer::handleAssetGetInfo(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) { void AssetServer::handleAssetGetInfo(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
QByteArray assetHash; QByteArray assetHash;
MessageID messageID; MessageID messageID;
@ -468,7 +488,7 @@ bool AssetServer::writeMappingsToFile() {
return false; return false;
} }
bool AssetServer::setMapping(AssetPath path, AssetHash hash) { bool AssetServer::setMapping(const AssetPath& path, const AssetHash& hash) {
// remember what the old mapping was in case persistence fails // remember what the old mapping was in case persistence fails
auto oldMapping = _fileMappings.value(path).toString(); auto oldMapping = _fileMappings.value(path).toString();
@ -516,3 +536,25 @@ bool AssetServer::deleteMappings(const AssetPathList& paths) {
return false; return false;
} }
} }
bool AssetServer::renameMapping(const AssetPath& oldPath, const AssetPath& newPath) {
// take the old hash to remove the old mapping
auto oldMapping = _fileMappings[oldPath].toString();
if (!oldMapping.isEmpty()) {
_fileMappings[newPath] = oldMapping;
if (writeMappingsToFile()) {
// persisted the renamed mapping, return success
return true;
} else {
// we couldn't persist the renamed mapping, rollback and return failure
_fileMappings[oldPath] = oldMapping;
return false;
}
} else {
// failed to find a mapping that was to be renamed, return failure
return false;
}
}

View file

@ -12,10 +12,10 @@
#ifndef hifi_AssetServer_h #ifndef hifi_AssetServer_h
#define hifi_AssetServer_h #define hifi_AssetServer_h
#include <QDir> #include <QtCore/QDir>
#include <QtCore/QThreadPool>
#include <ThreadedAssignment.h> #include <ThreadedAssignment.h>
#include <QThreadPool>
#include "AssetUtils.h" #include "AssetUtils.h"
#include "ReceivedMessage.h" #include "ReceivedMessage.h"
@ -45,18 +45,20 @@ private:
void handleGetAllMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); void handleGetAllMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
void handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); void handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
void handleDeleteMappingsOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); void handleDeleteMappingsOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
void handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
// Mapping file operations must be called from main assignment thread only // Mapping file operations must be called from main assignment thread only
void loadMappingsFromFile(); void loadMappingsFromFile();
bool writeMappingsToFile(); bool writeMappingsToFile();
/// Set the mapping for path to hash /// Set the mapping for path to hash
bool setMapping(AssetPath path, AssetHash hash); bool setMapping(const AssetPath& path, const AssetHash& hash);
/// Delete mapping `path`. Returns `true` if deletion of mappings succeeds, else `false`. /// Delete mapping `path`. Returns `true` if deletion of mappings succeeds, else `false`.
bool deleteMappings(const AssetPathList& paths); bool deleteMappings(const AssetPathList& paths);
static void writeError(NLPacketList* packetList, AssetServerError error); /// Rename mapping from `oldPath` to `newPath`. Returns true if successful
bool renameMapping(const AssetPath& oldPath, const AssetPath& newPath);
void performMappingMigration(); void performMappingMigration();
@ -67,8 +69,4 @@ private:
QThreadPool _taskPool; QThreadPool _taskPool;
}; };
inline void writeError(NLPacketList* packetList, AssetServerError error) {
packetList->writePrimitive(error);
}
#endif #endif

View file

@ -56,7 +56,7 @@ void SendAssetTask::run() {
replyPacketList->writePrimitive(messageID); replyPacketList->writePrimitive(messageID);
if (end <= start) { if (end <= start) {
writeError(replyPacketList.get(), AssetServerError::InvalidByteRange); replyPacketList->writePrimitive(AssetServerError::InvalidByteRange);
} else { } else {
QString filePath = _resourcesDir.filePath(QString(hexHash)); QString filePath = _resourcesDir.filePath(QString(hexHash));
@ -64,7 +64,7 @@ void SendAssetTask::run() {
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
if (file.size() < end) { if (file.size() < end) {
writeError(replyPacketList.get(), AssetServerError::InvalidByteRange); replyPacketList->writePrimitive(AssetServerError::InvalidByteRange);
qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end; qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end;
} else { } else {
auto size = end - start; auto size = end - start;
@ -77,7 +77,7 @@ void SendAssetTask::run() {
file.close(); file.close();
} else { } else {
qCDebug(networking) << "Asset not found: " << filePath << "(" << hexHash << ")"; qCDebug(networking) << "Asset not found: " << filePath << "(" << hexHash << ")";
writeError(replyPacketList.get(), AssetServerError::AssetNotFound); replyPacketList->writePrimitive(AssetServerError::AssetNotFound);
} }
} }

View file

@ -34,7 +34,7 @@ Window {
property var scripts: ScriptDiscoveryService; property var scripts: ScriptDiscoveryService;
property var scriptsModel: scripts.scriptsModelFilter property var scriptsModel: scripts.scriptsModelFilter
property var currentDirectory: "" property var currentDirectory;
Settings { Settings {
category: "Overlay.AssetServer" category: "Overlay.AssetServer"
@ -43,29 +43,96 @@ Window {
property alias directory: root.currentDirectory property alias directory: root.currentDirectory
} }
function reload() { function reload() {
print("reload"); print("reload");
} }
function addToWorld() {
function goBack() { print("addToWorld");
print("goBack");
} }
function renameFile() {
var path = scriptsModel.data(treeView.currentIndex, 0x100);
if (!path) {
return;
}
function uploadFile(fileUrl, addToScene) { var object = desktop.inputDialog({
print("uploadFile: " + fileUrl + " " + addToScene); label: "Enter new path:",
prefilledText: path,
placeholderText: "Enter path here"
});
object.selected.connect(function(destinationPath) {
console.log("Renaming " + path + " to " + destinationPath);
});
} }
function deleteFile() { function deleteFile() {
print("deleteFile"); var path = scriptsModel.data(treeView.currentIndex, 0x100);
if (!path) {
return;
}
var object = desktop.messageBox({
icon: OriginalDialogs.StandardIcon.Question,
buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No,
defaultButton: OriginalDialogs.StandardButton.No,
text: "Deleting",
informativeText: "You are about to delete the following file:\n" +
path +
"\nDo you want to continue?"
});
object.selected.connect(function(button) {
if (button === OriginalDialogs.StandardButton.Yes) {
console.log("Deleting " + path);
}
});
}
function chooseClicked() {
var browser = desktop.fileDialog({
selectDirectory: false,
dir: currentDirectory
});
browser.selectedFile.connect(function(url){
fileUrlTextField.text = fileDialogHelper.urlToPath(url);
currentDirectory = browser.dir;
});
}
function uploadClicked() {
var fileUrl = fileUrlTextField.text
var addToWorld = addToWorldCheckBox.checked
var path = scriptsModel.data(treeView.currentIndex, 0x100);
var directory = path ? path.slice(0, path.lastIndexOf('/') + 1) : "";
var filename = fileUrl.slice(fileUrl.lastIndexOf('/') + 1);
var object = desktop.inputDialog({
label: "Enter asset path:",
prefilledText: directory + filename,
placeholderText: "Enter path here"
});
object.selected.connect(function(destinationPath) {
console.log("Uploading " + fileUrl + " to " + destinationPath + " (addToWorld: " + addToWorld + ")");
});
} }
Column { Column {
width: pane.contentWidth width: pane.contentWidth
HifiControls.ContentSection { HifiControls.ContentSection {
id: assetDirectory
name: "Asset Directory" name: "Asset Directory"
spacing: hifi.dimensions.contentSpacing.y spacing: hifi.dimensions.contentSpacing.y
isFirst: true isFirst: true
@ -76,16 +143,6 @@ Window {
anchors.right: parent.right anchors.right: parent.right
spacing: hifi.dimensions.contentSpacing.x spacing: hifi.dimensions.contentSpacing.x
HifiControls.GlyphButton {
glyph: hifi.glyphs.back
color: hifi.buttons.white
colorScheme: root.colorScheme
height: 26
width: 26
onClicked: root.goBack()
}
HifiControls.GlyphButton { HifiControls.GlyphButton {
glyph: hifi.glyphs.reload glyph: hifi.glyphs.reload
color: hifi.buttons.white color: hifi.buttons.white
@ -95,23 +152,35 @@ Window {
onClicked: root.reload() onClicked: root.reload()
} }
}
Item { HifiControls.Button {
// Take the deleteButotn out of the column flow. text: "ADD TO WORLD"
id: deleteButtonContainer color: hifi.buttons.white
anchors.top: buttonRow.top colorScheme: root.colorScheme
anchors.right: parent.right height: 26
width: 120
onClicked: root.addToWorld()
}
HifiControls.Button {
text: "RENAME"
color: hifi.buttons.white
colorScheme: root.colorScheme
height: 26
width: 80
onClicked: root.renameFile()
}
HifiControls.Button { HifiControls.Button {
id: deleteButton id: deleteButton
anchors.right: parent.right
text: "DELETE SELECTION" text: "DELETE"
color: hifi.buttons.red color: hifi.buttons.red
colorScheme: root.colorScheme colorScheme: root.colorScheme
height: 26 height: 26
width: 130 width: 80
onClicked: root.deleteFile() onClicked: root.deleteFile()
} }
@ -119,7 +188,7 @@ Window {
HifiControls.Tree { HifiControls.Tree {
id: treeView id: treeView
height: 250 height: 400
treeModel: scriptsModel treeModel: scriptsModel
colorScheme: root.colorScheme colorScheme: root.colorScheme
anchors.left: parent.left anchors.left: parent.left
@ -128,16 +197,12 @@ Window {
} }
HifiControls.ContentSection { HifiControls.ContentSection {
id: uploadSection
name: "" name: ""
spacing: hifi.dimensions.contentSpacing.y spacing: hifi.dimensions.contentSpacing.y
Component {
id: fileBrowserBuilder;
FileDialog { selectDirectory: true }
}
HifiControls.TextField { HifiControls.TextField {
id: fileUrl id: fileUrlTextField
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: chooseButton.width + hifi.dimensions.contentSpacing.x anchors.rightMargin: chooseButton.width + hifi.dimensions.contentSpacing.x
@ -150,7 +215,7 @@ Window {
Item { Item {
// Take the chooseButton out of the column flow. // Take the chooseButton out of the column flow.
id: chooseButtonContainer id: chooseButtonContainer
anchors.top: fileUrl.top anchors.top: fileUrlTextField.top
anchors.right: parent.right anchors.right: parent.right
HifiControls.Button { HifiControls.Button {
@ -164,33 +229,24 @@ Window {
width: 100 width: 100
onClicked: { onClicked: root.chooseClicked()
var browser = fileBrowserBuilder.createObject(desktop, {
selectDirectory: true,
folder: fileDialogHelper.pathToUrl(currentDirectory)
});
browser.selectedFile.connect(function(url){
console.log(url);
fileUrl.text = fileDialogHelper.urlToPath(url);
});
}
} }
} }
HifiControls.CheckBox { HifiControls.CheckBox {
id: addToScene id: addToWorldCheckBox
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: uploadButton.width + hifi.dimensions.contentSpacing.x anchors.rightMargin: uploadButton.width + hifi.dimensions.contentSpacing.x
text: "Add to scene on upload" text: "Add to world on upload"
checked: false checked: false
} }
Item { Item {
// Take the uploadButton out of the column flow. // Take the uploadButton out of the column flow.
id: uploadButtonContainer id: uploadButtonContainer
anchors.top: addToScene.top anchors.top: addToWorldCheckBox.top
anchors.right: parent.right anchors.right: parent.right
HifiControls.Button { HifiControls.Button {
@ -203,10 +259,12 @@ Window {
height: 30 height: 30
width: 155 width: 155
enabled: fileUrl.text != "" enabled: fileUrlTextField.text != ""
onClicked: root.uploadFile(fileUrl.text, addToScene.checked) onClicked: root.uploadClicked()
} }
} }
HifiControls.VerticalSpacer {}
} }
} }
} }

View file

@ -39,6 +39,7 @@ ModalWindow {
// For text boxes // For text boxes
property alias placeholderText: textResult.placeholderText property alias placeholderText: textResult.placeholderText
property alias prefilledText: textResult.text
// For combo boxes // For combo boxes
property bool editable: true; property bool editable: true;

View file

@ -22,6 +22,7 @@
#include "AssetRequest.h" #include "AssetRequest.h"
#include "AssetUpload.h" #include "AssetUpload.h"
#include "AssetUtils.h" #include "AssetUtils.h"
#include "MappingRequest.h"
#include "NetworkAccessManager.h" #include "NetworkAccessManager.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "NodeList.h" #include "NodeList.h"
@ -30,151 +31,6 @@
MessageID AssetClient::_currentID = 0; MessageID AssetClient::_currentID = 0;
void MappingRequest::start() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
return;
}
doStart();
};
GetMappingRequest::GetMappingRequest(const AssetPath& path) : _path(path) {
};
void GetMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
// Check cache
auto it = assetClient->_mappingCache.constFind(_path);
if (it != assetClient->_mappingCache.constEnd()) {
_hash = it.value();
emit finished(this);
return;
}
assetClient->getAssetMapping(_path, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::AssetNotFound:
_error = NotFound;
break;
default:
_error = UnknownError;
break;
}
}
if (!_error) {
_hash = message->read(SHA256_HASH_LENGTH).toHex();
assetClient->_mappingCache[_path] = _hash;
}
emit finished(this);
});
};
GetAllMappingsRequest::GetAllMappingsRequest() {
};
void GetAllMappingsRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAllAssetMappings([this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
default:
_error = UnknownError;
break;
}
}
if (!error) {
int numberOfMappings;
message->readPrimitive(&numberOfMappings);
assetClient->_mappingCache.clear();
for (auto i = 0; i < numberOfMappings; ++i) {
auto path = message->readString();
auto hash = message->readString();
_mappings[path] = hash;
assetClient->_mappingCache[path] = hash;
}
}
emit finished(this);
});
};
SetMappingRequest::SetMappingRequest(const AssetPath& path, const AssetHash& hash) : _path(path), _hash(hash) {
};
void SetMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->setAssetMapping(_path, _hash, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::PermissionDenied:
_error = PermissionDenied;
break;
default:
_error = UnknownError;
break;
}
}
if (!error) {
assetClient->_mappingCache[_path] = _hash;
}
emit finished(this);
});
};
DeleteMappingsRequest::DeleteMappingsRequest(const AssetPathList& paths) : _paths(paths) {
};
void DeleteMappingsRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->deleteAssetMappings(_paths, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::PermissionDenied:
_error = PermissionDenied;
break;
default:
_error = UnknownError;
break;
}
}
if (!error) {
// enumerate the paths and remove them from the cache
for (auto& path : _paths) {
assetClient->_mappingCache.remove(path);
}
}
emit finished(this);
});
};
AssetClient::AssetClient() { AssetClient::AssetClient() {
setCustomDeleter([](Dependency* dependency){ setCustomDeleter([](Dependency* dependency){
@ -300,6 +156,10 @@ SetMappingRequest* AssetClient::createSetMappingRequest(const AssetPath& path, c
return new SetMappingRequest(path, hash); return new SetMappingRequest(path, hash);
} }
RenameMappingRequest* AssetClient::createRenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath) {
return new RenameMappingRequest(oldPath, newPath);
}
AssetRequest* AssetClient::createRequest(const AssetHash& hash) { AssetRequest* AssetClient::createRequest(const AssetHash& hash) {
if (hash.length() != SHA256_HASH_HEX_LENGTH) { if (hash.length() != SHA256_HASH_HEX_LENGTH) {
qCWarning(asset_client) << "Invalid hash size"; qCWarning(asset_client) << "Invalid hash size";
@ -573,7 +433,7 @@ bool AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, Ma
packetList->writePrimitive(AssetMappingOperationType::Set); packetList->writePrimitive(AssetMappingOperationType::Set);
packetList->writeString(path.toUtf8()); packetList->writeString(path);
packetList->write(QByteArray::fromHex(hash.toUtf8())); packetList->write(QByteArray::fromHex(hash.toUtf8()));
nodeList->sendPacketList(std::move(packetList), *assetServer); nodeList->sendPacketList(std::move(packetList), *assetServer);
@ -586,6 +446,32 @@ bool AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, Ma
return false; return false;
} }
bool AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback) {
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (assetServer) {
auto packetList = NLPacketList::create(PacketType::AssetMappingOperation, QByteArray(), true, true);
auto messageID = ++_currentID;
packetList->writePrimitive(messageID);
packetList->writePrimitive(AssetMappingOperationType::Rename);
packetList->writeString(oldPath);
packetList->writeString(newPath);
nodeList->sendPacketList(std::move(packetList), *assetServer);
_pendingMappingRequests[assetServer][messageID] = callback;
return true;
}
return false;
}
bool AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) { bool AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
@ -695,140 +581,3 @@ void AssetClient::handleNodeKilled(SharedNodePointer node) {
_mappingCache.clear(); _mappingCache.clear();
} }
void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) {
QByteArray dataByteArray = data.toUtf8();
auto upload = DependencyManager::get<AssetClient>()->createUpload(dataByteArray);
if (!upload) {
qCWarning(asset_client) << "Error uploading file to asset server";
return;
}
QObject::connect(upload, &AssetUpload::finished, this, [this, callback](AssetUpload* upload, const QString& hash) mutable {
if (callback.isFunction()) {
QString url = "atp://" + hash;
QScriptValueList args { url };
callback.call(_engine->currentContext()->thisObject(), args);
}
});
upload->start();
}
AssetScriptingInterface::AssetScriptingInterface(QScriptEngine* engine) :
_engine(engine)
{
}
void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) {
const QString ATP_SCHEME { "atp://" };
if (!urlString.startsWith(ATP_SCHEME)) {
return;
}
// Make request to atp
auto path = urlString.right(urlString.length() - ATP_SCHEME.length());
auto parts = path.split(".", QString::SkipEmptyParts);
auto hash = parts.length() > 0 ? parts[0] : "";
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
return;
}
auto assetClient = DependencyManager::get<AssetClient>();
auto assetRequest = assetClient->createRequest(hash);
if (!assetRequest) {
return;
}
_pendingRequests << assetRequest;
connect(assetRequest, &AssetRequest::finished, this, [this, callback](AssetRequest* request) mutable {
Q_ASSERT(request->getState() == AssetRequest::Finished);
if (request->getError() == AssetRequest::Error::NoError) {
if (callback.isFunction()) {
QString data = QString::fromUtf8(request->getData());
QScriptValueList args { data };
callback.call(_engine->currentContext()->thisObject(), args);
}
}
request->deleteLater();
_pendingRequests.remove(request);
});
assetRequest->start();
}
void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createSetMappingRequest(path, hash);
connect(request, &SetMappingRequest::finished, this, [this, callback](SetMappingRequest* request) mutable {
QScriptValueList args { uint8_t(request->getError()) };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}
void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createGetMappingRequest(path);
connect(request, &GetMappingRequest::finished, this, [this, callback](GetMappingRequest* request) mutable {
QScriptValueList args { uint8_t(request->getError()), request->getHash() };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}
void AssetScriptingInterface::deleteMappings(QStringList paths, QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createDeleteMappingsRequest(paths);
connect(request, &DeleteMappingsRequest::finished, this, [this, callback](DeleteMappingsRequest* request) mutable {
QScriptValueList args { uint8_t(request->getError()) };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}
void AssetScriptingInterface::getAllMappings(QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createGetAllMappingsRequest();
connect(request, &GetAllMappingsRequest::finished, this, [this, callback](GetAllMappingsRequest* request) mutable {
auto mappings = request->getMappings();
auto map = callback.engine()->newObject();
for (auto& kv : mappings ) {
map.setProperty(kv.first, kv.second);
}
QScriptValueList args { uint8_t(request->getError()), map };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}

View file

@ -9,12 +9,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#ifndef hifi_AssetClient_h #ifndef hifi_AssetClient_h
#define hifi_AssetClient_h #define hifi_AssetClient_h
#include <QString> #include <QString>
#include <QScriptValue>
#include <map> #include <map>
@ -29,6 +27,9 @@
class GetMappingRequest; class GetMappingRequest;
class SetMappingRequest; class SetMappingRequest;
class GetAllMappingsRequest;
class DeleteMappingsRequest;
class RenameMappingRequest;
class AssetRequest; class AssetRequest;
class AssetUpload; class AssetUpload;
@ -43,92 +44,6 @@ using GetInfoCallback = std::function<void(bool responseReceived, AssetServerErr
using UploadResultCallback = std::function<void(bool responseReceived, AssetServerError serverError, const QString& hash)>; using UploadResultCallback = std::function<void(bool responseReceived, AssetServerError serverError, const QString& hash)>;
using ProgressCallback = std::function<void(qint64 totalReceived, qint64 total)>; using ProgressCallback = std::function<void(qint64 totalReceived, qint64 total)>;
class MappingRequest : public QObject {
Q_OBJECT
public:
enum Error {
NoError,
NotFound,
NetworkError,
PermissionDenied,
UnknownError
};
Q_INVOKABLE void start();
Error getError() const { return _error; }
protected:
Error _error { NoError };
private:
virtual void doStart() = 0;
};
class GetMappingRequest : public MappingRequest {
Q_OBJECT
public:
GetMappingRequest(const AssetPath& path);
AssetHash getHash() const { return _hash; }
signals:
void finished(GetMappingRequest* thisRequest);
private:
virtual void doStart() override;
AssetPath _path;
AssetHash _hash;
};
class SetMappingRequest : public MappingRequest {
Q_OBJECT
public:
SetMappingRequest(const AssetPath& path, const AssetHash& hash);
AssetHash getHash() const { return _hash; }
signals:
void finished(SetMappingRequest* thisRequest);
private:
virtual void doStart() override;
AssetPath _path;
AssetHash _hash;
};
class DeleteMappingsRequest : public MappingRequest {
Q_OBJECT
public:
DeleteMappingsRequest(const AssetPathList& path);
signals:
void finished(DeleteMappingsRequest* thisRequest);
private:
virtual void doStart() override;
AssetPathList _paths;
};
class GetAllMappingsRequest : public MappingRequest {
Q_OBJECT
public:
GetAllMappingsRequest();
AssetMapping getMappings() const { return _mappings; }
signals:
void finished(GetAllMappingsRequest* thisRequest);
private:
virtual void doStart() override;
std::map<AssetPath, AssetHash> _mappings;
};
class AssetClient : public QObject, public Dependency { class AssetClient : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
public: public:
@ -138,6 +53,7 @@ public:
Q_INVOKABLE GetAllMappingsRequest* createGetAllMappingsRequest(); Q_INVOKABLE GetAllMappingsRequest* createGetAllMappingsRequest();
Q_INVOKABLE DeleteMappingsRequest* createDeleteMappingsRequest(const AssetPathList& paths); Q_INVOKABLE DeleteMappingsRequest* createDeleteMappingsRequest(const AssetPathList& paths);
Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash); Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash);
Q_INVOKABLE RenameMappingRequest* createRenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath);
Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash); Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash);
Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data);
@ -161,6 +77,7 @@ private:
bool getAllAssetMappings(MappingOperationCallback callback); bool getAllAssetMappings(MappingOperationCallback callback);
bool setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback); bool setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback);
bool deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback); bool deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback);
bool renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback);
bool getAssetInfo(const QString& hash, GetInfoCallback callback); bool getAssetInfo(const QString& hash, GetInfoCallback callback);
bool getAsset(const QString& hash, DataOffset start, DataOffset end, bool getAsset(const QString& hash, DataOffset start, DataOffset end,
@ -186,24 +103,7 @@ private:
friend class GetAllMappingsRequest; friend class GetAllMappingsRequest;
friend class SetMappingRequest; friend class SetMappingRequest;
friend class DeleteMappingsRequest; friend class DeleteMappingsRequest;
friend class RenameMappingRequest;
}; };
class AssetScriptingInterface : public QObject {
Q_OBJECT
public:
AssetScriptingInterface(QScriptEngine* engine);
Q_INVOKABLE void uploadData(QString data, QScriptValue callback);
Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete);
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
Q_INVOKABLE void getMapping(QString path, QScriptValue callback);
Q_INVOKABLE void deleteMappings(QStringList paths, QScriptValue callback);
Q_INVOKABLE void getAllMappings(QScriptValue callback);
protected:
QSet<AssetRequest*> _pendingRequests;
QScriptEngine* _engine;
};
#endif #endif

View file

@ -13,6 +13,7 @@
#include "AssetClient.h" #include "AssetClient.h"
#include "AssetUtils.h" #include "AssetUtils.h"
#include "MappingRequest.h"
AssetResourceRequest::~AssetResourceRequest() { AssetResourceRequest::~AssetResourceRequest() {
if (_assetMappingRequest) { if (_assetMappingRequest) {

View file

@ -44,7 +44,8 @@ enum AssetMappingOperationType : uint8_t {
Get = 0, Get = 0,
GetAll, GetAll,
Set, Set,
Delete Delete,
Rename
}; };
QUrl getATPUrl(const QString& hash); QUrl getATPUrl(const QString& hash);

View file

@ -0,0 +1,202 @@
//
// MappingRequest.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 2016-03-08.
// Copyright 2016 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 "MappingRequest.h"
#include <QtCore/QThread>
#include <DependencyManager.h>
#include "AssetClient.h"
void MappingRequest::start() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
return;
}
doStart();
};
GetMappingRequest::GetMappingRequest(const AssetPath& path) : _path(path) {
};
void GetMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
// Check cache
auto it = assetClient->_mappingCache.constFind(_path);
if (it != assetClient->_mappingCache.constEnd()) {
_hash = it.value();
emit finished(this);
return;
}
assetClient->getAssetMapping(_path, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::AssetNotFound:
_error = NotFound;
break;
default:
_error = UnknownError;
break;
}
}
if (!_error) {
_hash = message->read(SHA256_HASH_LENGTH).toHex();
assetClient->_mappingCache[_path] = _hash;
}
emit finished(this);
});
};
GetAllMappingsRequest::GetAllMappingsRequest() {
};
void GetAllMappingsRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->getAllAssetMappings([this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
default:
_error = UnknownError;
break;
}
}
if (!error) {
int numberOfMappings;
message->readPrimitive(&numberOfMappings);
assetClient->_mappingCache.clear();
for (auto i = 0; i < numberOfMappings; ++i) {
auto path = message->readString();
auto hash = message->readString();
_mappings[path] = hash;
assetClient->_mappingCache[path] = hash;
}
}
emit finished(this);
});
};
SetMappingRequest::SetMappingRequest(const AssetPath& path, const AssetHash& hash) : _path(path), _hash(hash) {
};
void SetMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->setAssetMapping(_path, _hash, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::PermissionDenied:
_error = PermissionDenied;
break;
default:
_error = UnknownError;
break;
}
}
if (!error) {
assetClient->_mappingCache[_path] = _hash;
}
emit finished(this);
});
};
DeleteMappingsRequest::DeleteMappingsRequest(const AssetPathList& paths) : _paths(paths) {
};
void DeleteMappingsRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->deleteAssetMappings(_paths, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::PermissionDenied:
_error = PermissionDenied;
break;
default:
_error = UnknownError;
break;
}
}
if (!error) {
// enumerate the paths and remove them from the cache
for (auto& path : _paths) {
assetClient->_mappingCache.remove(path);
}
}
emit finished(this);
});
};
RenameMappingRequest::RenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath) :
_oldPath(oldPath),
_newPath(newPath)
{
}
void RenameMappingRequest::doStart() {
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->renameAssetMapping(_oldPath, _newPath, [this, assetClient](bool responseReceived,
AssetServerError error,
QSharedPointer<ReceivedMessage> message) {
if (!responseReceived) {
_error = NetworkError;
} else {
switch (error) {
case AssetServerError::NoError:
_error = NoError;
break;
case AssetServerError::PermissionDenied:
_error = PermissionDenied;
break;
default:
_error = UnknownError;
break;
}
}
if (!error) {
// take the hash mapped for the old path from the cache
auto hash = assetClient->_mappingCache.take(_oldPath);
if (!hash.isEmpty()) {
// use the hash mapped for the old path for the new path
assetClient->_mappingCache[_newPath] = hash;
}
}
emit finished(this);
});
}

View file

@ -0,0 +1,123 @@
//
// MappingRequest.h
// libraries/networking/src
//
// Created by Stephen Birarda on 2016-03-08.
// Copyright 2016 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_MappingRequest_h
#define hifi_MappingRequest_h
#include <QtCore/QObject>
#include "AssetUtils.h"
class MappingRequest : public QObject {
Q_OBJECT
public:
enum Error {
NoError,
NotFound,
NetworkError,
PermissionDenied,
UnknownError
};
Q_INVOKABLE void start();
Error getError() const { return _error; }
protected:
Error _error { NoError };
private:
virtual void doStart() = 0;
};
class GetMappingRequest : public MappingRequest {
Q_OBJECT
public:
GetMappingRequest(const AssetPath& path);
AssetHash getHash() const { return _hash; }
signals:
void finished(GetMappingRequest* thisRequest);
private:
virtual void doStart() override;
AssetPath _path;
AssetHash _hash;
};
class SetMappingRequest : public MappingRequest {
Q_OBJECT
public:
SetMappingRequest(const AssetPath& path, const AssetHash& hash);
AssetHash getHash() const { return _hash; }
signals:
void finished(SetMappingRequest* thisRequest);
private:
virtual void doStart() override;
AssetPath _path;
AssetHash _hash;
};
class DeleteMappingsRequest : public MappingRequest {
Q_OBJECT
public:
DeleteMappingsRequest(const AssetPathList& path);
signals:
void finished(DeleteMappingsRequest* thisRequest);
private:
virtual void doStart() override;
AssetPathList _paths;
};
class RenameMappingRequest : public MappingRequest {
Q_OBJECT
public:
RenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath);
signals:
void finished(RenameMappingRequest* thisRequest);
private:
virtual void doStart() override;
AssetPath _oldPath;
AssetPath _newPath;
};
class GetAllMappingsRequest : public MappingRequest {
Q_OBJECT
public:
GetAllMappingsRequest();
AssetMapping getMappings() const { return _mappings; }
signals:
void finished(GetAllMappingsRequest* thisRequest);
private:
virtual void doStart() override;
std::map<AssetPath, AssetHash> _mappings;
};
#endif // hifi_MappingRequest_h

View file

@ -0,0 +1,173 @@
//
// AssetScriptingInterface.cpp
// libraries/script-engine/src
//
// Created by Stephen Birarda on 2016-03-08.
// Copyright 2016 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 "AssetScriptingInterface.h"
#include <QtScript/QScriptEngine>
#include <AssetRequest.h>
#include <AssetUpload.h>
#include <MappingRequest.h>
#include <NetworkLogging.h>
AssetScriptingInterface::AssetScriptingInterface(QScriptEngine* engine) :
_engine(engine)
{
}
void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) {
QByteArray dataByteArray = data.toUtf8();
auto upload = DependencyManager::get<AssetClient>()->createUpload(dataByteArray);
if (!upload) {
qCWarning(asset_client) << "Error uploading file to asset server";
return;
}
QObject::connect(upload, &AssetUpload::finished, this, [this, callback](AssetUpload* upload, const QString& hash) mutable {
if (callback.isFunction()) {
QString url = "atp://" + hash;
QScriptValueList args { url };
callback.call(_engine->currentContext()->thisObject(), args);
}
});
upload->start();
}
void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) {
const QString ATP_SCHEME { "atp://" };
if (!urlString.startsWith(ATP_SCHEME)) {
return;
}
// Make request to atp
auto path = urlString.right(urlString.length() - ATP_SCHEME.length());
auto parts = path.split(".", QString::SkipEmptyParts);
auto hash = parts.length() > 0 ? parts[0] : "";
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
return;
}
auto assetClient = DependencyManager::get<AssetClient>();
auto assetRequest = assetClient->createRequest(hash);
if (!assetRequest) {
return;
}
_pendingRequests << assetRequest;
connect(assetRequest, &AssetRequest::finished, this, [this, callback](AssetRequest* request) mutable {
Q_ASSERT(request->getState() == AssetRequest::Finished);
if (request->getError() == AssetRequest::Error::NoError) {
if (callback.isFunction()) {
QString data = QString::fromUtf8(request->getData());
QScriptValueList args { data };
callback.call(_engine->currentContext()->thisObject(), args);
}
}
request->deleteLater();
_pendingRequests.remove(request);
});
assetRequest->start();
}
void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createSetMappingRequest(path, hash);
connect(request, &SetMappingRequest::finished, this, [this, callback](SetMappingRequest* request) mutable {
QScriptValueList args { uint8_t(request->getError()) };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}
void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createGetMappingRequest(path);
connect(request, &GetMappingRequest::finished, this, [this, callback](GetMappingRequest* request) mutable {
QScriptValueList args { uint8_t(request->getError()), request->getHash() };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}
void AssetScriptingInterface::deleteMappings(QStringList paths, QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createDeleteMappingsRequest(paths);
connect(request, &DeleteMappingsRequest::finished, this, [this, callback](DeleteMappingsRequest* request) mutable {
QScriptValueList args { uint8_t(request->getError()) };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}
void AssetScriptingInterface::getAllMappings(QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createGetAllMappingsRequest();
connect(request, &GetAllMappingsRequest::finished, this, [this, callback](GetAllMappingsRequest* request) mutable {
auto mappings = request->getMappings();
auto map = callback.engine()->newObject();
for (auto& kv : mappings ) {
map.setProperty(kv.first, kv.second);
}
QScriptValueList args { uint8_t(request->getError()), map };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}
void AssetScriptingInterface::renameMapping(QString oldPath, QString newPath, QScriptValue callback) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createRenameMappingRequest(oldPath, newPath);
connect(request, &RenameMappingRequest::finished, this, [this, callback](RenameMappingRequest* request) mutable {
QScriptValueList args { uint8_t(request->getError()) };
callback.call(_engine->currentContext()->thisObject(), args);
request->deleteLater();
});
request->start();
}

View file

@ -0,0 +1,39 @@
//
// AssetScriptingInterface.h
// libraries/script-engine/src
//
// Created by Stephen Birarda on 2016-03-08.
// Copyright 2016 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_AssetScriptingInterface_h
#define hifi_AssetScriptingInterface_h
#include <QtCore/QObject>
#include <QtScript/QScriptValue>
#include <AssetClient.h>
class AssetScriptingInterface : public QObject {
Q_OBJECT
public:
AssetScriptingInterface(QScriptEngine* engine);
Q_INVOKABLE void uploadData(QString data, QScriptValue callback);
Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete);
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
Q_INVOKABLE void getMapping(QString path, QScriptValue callback);
Q_INVOKABLE void deleteMappings(QStringList paths, QScriptValue callback);
Q_INVOKABLE void getAllMappings(QScriptValue callback);
Q_INVOKABLE void renameMapping(QString oldPath, QString newPath, QScriptValue callback);
protected:
QSet<AssetRequest*> _pendingRequests;
QScriptEngine* _engine;
};
#endif // hifi_AssetScriptingInterface_h

View file

@ -23,7 +23,6 @@
#include <AnimationCache.h> #include <AnimationCache.h>
#include <AnimVariant.h> #include <AnimVariant.h>
#include <AssetClient.h>
#include <AvatarData.h> #include <AvatarData.h>
#include <AvatarHashMap.h> #include <AvatarHashMap.h>
#include <LimitedNodeList.h> #include <LimitedNodeList.h>
@ -32,6 +31,7 @@
#include "MouseEvent.h" #include "MouseEvent.h"
#include "ArrayBufferClass.h" #include "ArrayBufferClass.h"
#include "AssetScriptingInterface.h"
#include "AudioScriptingInterface.h" #include "AudioScriptingInterface.h"
#include "Quat.h" #include "Quat.h"
#include "ScriptCache.h" #include "ScriptCache.h"
@ -219,4 +219,4 @@ protected:
static std::atomic<bool> _stoppingAllScripts; static std::atomic<bool> _stoppingAllScripts;
}; };
#endif // hifi_ScriptEngine_h #endif // hifi_ScriptEngine_h