mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 12:37:51 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into toulouse
This commit is contained in:
commit
7948fb266e
41 changed files with 574 additions and 198 deletions
|
@ -71,7 +71,7 @@ void MixerAvatar::fetchAvatarFST() {
|
||||||
connect(fstRequest, &ResourceRequest::finished, this, &MixerAvatar::fstRequestComplete);
|
connect(fstRequest, &ResourceRequest::finished, this, &MixerAvatar::fstRequestComplete);
|
||||||
fstRequest->send();
|
fstRequest->send();
|
||||||
} else {
|
} else {
|
||||||
qCDebug(avatars) << "Couldn't create FST request for" << avatarURL;
|
qCDebug(avatars) << "Couldn't create FST request for" << avatarURL << getSessionUUID();
|
||||||
_verifyState = error;
|
_verifyState = error;
|
||||||
}
|
}
|
||||||
_needsIdentityUpdate = true;
|
_needsIdentityUpdate = true;
|
||||||
|
@ -84,7 +84,7 @@ void MixerAvatar::fstRequestComplete() {
|
||||||
auto result = fstRequest->getResult();
|
auto result = fstRequest->getResult();
|
||||||
if (result != ResourceRequest::Success) {
|
if (result != ResourceRequest::Success) {
|
||||||
_verifyState = error;
|
_verifyState = error;
|
||||||
qCDebug(avatars) << "FST request for" << fstRequest->getUrl() << "failed:" << result;
|
qCDebug(avatars) << "FST request for" << fstRequest->getUrl() << "(user " << getSessionUUID() << ") failed:" << result;
|
||||||
} else {
|
} else {
|
||||||
_avatarFSTContents = fstRequest->getData();
|
_avatarFSTContents = fstRequest->getData();
|
||||||
_verifyState = receivedFST;
|
_verifyState = receivedFST;
|
||||||
|
@ -178,7 +178,8 @@ void MixerAvatar::ownerRequestComplete() {
|
||||||
} else {
|
} else {
|
||||||
auto jsonData = QJsonDocument::fromJson(networkReply->readAll())["data"];
|
auto jsonData = QJsonDocument::fromJson(networkReply->readAll())["data"];
|
||||||
if (!jsonData.isUndefined() && !jsonData.toObject()["message"].isUndefined()) {
|
if (!jsonData.isUndefined() && !jsonData.toObject()["message"].isUndefined()) {
|
||||||
qCDebug(avatars) << "Owner lookup failed for" << getDisplayName() << ":"
|
qCDebug(avatars) << "Owner lookup failed for" << getDisplayName() << "("
|
||||||
|
<< getSessionUUID() << ") :"
|
||||||
<< jsonData.toObject()["message"].toString();
|
<< jsonData.toObject()["message"].toString();
|
||||||
_verifyState = error;
|
_verifyState = error;
|
||||||
_pendingEvent = false;
|
_pendingEvent = false;
|
||||||
|
@ -221,7 +222,7 @@ void MixerAvatar::processCertifyEvents() {
|
||||||
} else {
|
} else {
|
||||||
_needsIdentityUpdate = true;
|
_needsIdentityUpdate = true;
|
||||||
_pendingEvent = false;
|
_pendingEvent = false;
|
||||||
qCDebug(avatars) << "Avatar" << getDisplayName() << "FAILED static certification";
|
qCDebug(avatars) << "Avatar" << getDisplayName() << "(" << getSessionUUID() << ") FAILED static certification";
|
||||||
}
|
}
|
||||||
} else { // FST doesn't have a certificate, so noncertified rather than failed:
|
} else { // FST doesn't have a certificate, so noncertified rather than failed:
|
||||||
_pendingEvent = false;
|
_pendingEvent = false;
|
||||||
|
@ -261,6 +262,8 @@ void MixerAvatar::processCertifyEvents() {
|
||||||
_pendingEvent = true;
|
_pendingEvent = true;
|
||||||
} else {
|
} else {
|
||||||
_verifyState = error;
|
_verifyState = error;
|
||||||
|
qCDebug(avatars) << "Get owner status - couldn't parse response for" << getSessionUUID()
|
||||||
|
<< ":" << _dynamicMarketResponse;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCDebug(avatars) << "Get owner status failed for " << getDisplayName() << _marketplaceIdFromURL <<
|
qCDebug(avatars) << "Get owner status failed for " << getDisplayName() << _marketplaceIdFromURL <<
|
||||||
|
@ -332,7 +335,7 @@ void MixerAvatar::sendOwnerChallenge() {
|
||||||
nonceHash.addData(nonce);
|
nonceHash.addData(nonce);
|
||||||
_challengeNonceHash = nonceHash.result();
|
_challengeNonceHash = nonceHash.result();
|
||||||
|
|
||||||
static constexpr int CHALLENGE_TIMEOUT_MS = 5 * 1000; // 5 s
|
static constexpr int CHALLENGE_TIMEOUT_MS = 10 * 1000; // 10 s
|
||||||
if (_challengeTimeout) {
|
if (_challengeTimeout) {
|
||||||
_challengeTimeout->deleteLater();
|
_challengeTimeout->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -344,6 +347,7 @@ void MixerAvatar::sendOwnerChallenge() {
|
||||||
_pendingEvent = false;
|
_pendingEvent = false;
|
||||||
_verifyState = verificationFailed;
|
_verifyState = verificationFailed;
|
||||||
_needsIdentityUpdate = true;
|
_needsIdentityUpdate = true;
|
||||||
|
qCDebug(avatars) << "Dynamic verification TIMED-OUT for " << getDisplayName() << getSessionUUID();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_challengeTimeout->start();
|
_challengeTimeout->start();
|
||||||
|
|
|
@ -1788,6 +1788,39 @@
|
||||||
"default": false
|
"default": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "installed_content",
|
||||||
|
"label": "Installed Content",
|
||||||
|
"hidden": true,
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"name": "filename",
|
||||||
|
"content_setting": true,
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"content_setting": true,
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "creation_time",
|
||||||
|
"content_setting": true,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "install_time",
|
||||||
|
"type": "int",
|
||||||
|
"content_setting": true,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "installed_by",
|
||||||
|
"content_setting": true,
|
||||||
|
"default": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,14 @@ $(document).ready(function(){
|
||||||
var RESTORE_SETTINGS_FILE_ID = 'restore-settings-file';
|
var RESTORE_SETTINGS_FILE_ID = 'restore-settings-file';
|
||||||
var UPLOAD_CONTENT_ALLOWED_DIV_ID = 'upload-content-allowed';
|
var UPLOAD_CONTENT_ALLOWED_DIV_ID = 'upload-content-allowed';
|
||||||
var UPLOAD_CONTENT_RECOVERING_DIV_ID = 'upload-content-recovering';
|
var UPLOAD_CONTENT_RECOVERING_DIV_ID = 'upload-content-recovering';
|
||||||
|
var INSTALLED_CONTENT_FILENAME_ID = 'installed-content-filename';
|
||||||
|
var INSTALLED_CONTENT_NAME_ID = 'installed-content-name';
|
||||||
|
var INSTALLED_CONTENT_CREATED_ID = 'installed-content-created';
|
||||||
|
var INSTALLED_CONTENT_INSTALLED_ID = 'installed-content-installed';
|
||||||
|
var INSTALLED_CONTENT_INSTALLED_BY_ID = 'installed-content-installed-by';
|
||||||
|
|
||||||
var isRestoring = false;
|
var isRestoring = false;
|
||||||
|
var restoreErrorShown = false;
|
||||||
|
|
||||||
function progressBarHTML(extraClass, label) {
|
function progressBarHTML(extraClass, label) {
|
||||||
var html = "<div class='progress'>";
|
var html = "<div class='progress'>";
|
||||||
|
@ -64,11 +70,23 @@ $(document).ready(function(){
|
||||||
|
|
||||||
var ajaxObject = $.ajax(ajaxParams);
|
var ajaxObject = $.ajax(ajaxParams);
|
||||||
ajaxObject.fail(function (jqXHR, textStatus, errorThrown) {
|
ajaxObject.fail(function (jqXHR, textStatus, errorThrown) {
|
||||||
showErrorMessage(
|
// status of 0 means the connection was reset, which
|
||||||
"Error",
|
// happens after the content is parsed and the server restarts
|
||||||
"There was a problem restoring domain content.\n"
|
// in the case of json and json.gz files
|
||||||
+ "Please ensure that the content archive or entity file is valid and try again."
|
if (jqXHR.status != 0) {
|
||||||
);
|
showErrorMessage(
|
||||||
|
"Error",
|
||||||
|
"There was a problem restoring domain content.\n"
|
||||||
|
+ "Please ensure that the content archive or entity file is valid and try again."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
isRestoring = true;
|
||||||
|
|
||||||
|
// immediately reload backup information since one should be restoring now
|
||||||
|
reloadBackupInformation();
|
||||||
|
|
||||||
|
swal.close();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
updateProgressBars($('.upload-content-progress'), (offset + nextChunkSize) * 100 / fileSize);
|
updateProgressBars($('.upload-content-progress'), (offset + nextChunkSize) * 100 / fileSize);
|
||||||
|
@ -103,10 +121,25 @@ $(document).ready(function(){
|
||||||
html += "<span class='help-block'>Restore in progress</span>";
|
html += "<span class='help-block'>Restore in progress</span>";
|
||||||
html += progressBarHTML('recovery', 'Restoring');
|
html += progressBarHTML('recovery', 'Restoring');
|
||||||
html += "</div></div>";
|
html += "</div></div>";
|
||||||
|
|
||||||
$('#' + Settings.UPLOAD_CONTENT_BACKUP_PANEL_ID + ' .panel-body').html(html);
|
$('#' + Settings.UPLOAD_CONTENT_BACKUP_PANEL_ID + ' .panel-body').html(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupInstalledContentInfo() {
|
||||||
|
var html = "<table class='table table-bordered'><tbody>";
|
||||||
|
html += "<tr class='headers'><td class='data'><strong>Name</strong></td>";
|
||||||
|
html += "<td class='data'><strong>File Name</strong></td>";
|
||||||
|
html += "<td class='data'><strong>Created</strong></td>";
|
||||||
|
html += "<td class='data'><strong>Installed</strong></td>";
|
||||||
|
//html += "<td class='data'><strong>Installed By</strong></td></tr>";
|
||||||
|
html += "<tr><td class='data' id='" + INSTALLED_CONTENT_NAME_ID + "'/>";
|
||||||
|
html += "<td class='data' id='" + INSTALLED_CONTENT_FILENAME_ID + "'/>";
|
||||||
|
html += "<td class='data' id='" + INSTALLED_CONTENT_CREATED_ID + "'/>";
|
||||||
|
html += "<td class='data' id='" + INSTALLED_CONTENT_INSTALLED_ID + "'/>";
|
||||||
|
//html += "<td class='data' id='" + INSTALLED_CONTENT_INSTALLED_BY_ID + "'/></tr>";
|
||||||
|
html += "</tbody></table>";
|
||||||
|
$('#' + Settings.INSTALLED_CONTENT + ' .panel-body').html(html);
|
||||||
|
}
|
||||||
|
|
||||||
// handle content archive or entity file upload
|
// handle content archive or entity file upload
|
||||||
|
|
||||||
// when the selected file is changed, enable the button if there's a selected file
|
// when the selected file is changed, enable the button if there's a selected file
|
||||||
|
@ -135,6 +168,7 @@ $(document).ready(function(){
|
||||||
|
|
||||||
var GENERATE_ARCHIVE_BUTTON_ID = 'generate-archive-button';
|
var GENERATE_ARCHIVE_BUTTON_ID = 'generate-archive-button';
|
||||||
var CONTENT_ARCHIVES_NORMAL_ID = 'content-archives-success';
|
var CONTENT_ARCHIVES_NORMAL_ID = 'content-archives-success';
|
||||||
|
var CONTENT_ARCHIVES_CONTENT_INFO_ID = 'content-archives-content-info';
|
||||||
var CONTENT_ARCHIVES_ERROR_ID = 'content-archives-error';
|
var CONTENT_ARCHIVES_ERROR_ID = 'content-archives-error';
|
||||||
var AUTOMATIC_ARCHIVES_TABLE_ID = 'automatic-archives-table';
|
var AUTOMATIC_ARCHIVES_TABLE_ID = 'automatic-archives-table';
|
||||||
var AUTOMATIC_ARCHIVES_TBODY_ID = 'automatic-archives-tbody';
|
var AUTOMATIC_ARCHIVES_TBODY_ID = 'automatic-archives-tbody';
|
||||||
|
@ -230,13 +264,27 @@ $(document).ready(function(){
|
||||||
url: '/api/backups',
|
url: '/api/backups',
|
||||||
cache: false
|
cache: false
|
||||||
}).done(function(data) {
|
}).done(function(data) {
|
||||||
|
|
||||||
// split the returned data into manual and automatic manual backups
|
// split the returned data into manual and automatic manual backups
|
||||||
var splitBackups = _.partition(data.backups, function(value, index) {
|
var splitBackups = _.partition(data.backups, function(value, index) {
|
||||||
return value.isManualBackup;
|
return value.isManualBackup;
|
||||||
});
|
});
|
||||||
|
if (data.status.recoveryError && !restoreErrorShown) {
|
||||||
if (isRestoring && !data.status.isRecovering) {
|
restoreErrorShown = true;
|
||||||
|
swal({
|
||||||
|
title: "Error",
|
||||||
|
text: "There was a problem restoring domain content.\n"
|
||||||
|
+ data.status.recoveryError,
|
||||||
|
type: "error",
|
||||||
|
showCancelButton: false,
|
||||||
|
confirmButtonText: "Restart",
|
||||||
|
closeOnConfirm: true,
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
$.get("/restart");
|
||||||
|
showRestartModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isRestoring && !data.status.isRecovering && !data.status.recoveryError) {
|
||||||
// we were recovering and we finished - the DS is going to restart so show the restart modal
|
// we were recovering and we finished - the DS is going to restart so show the restart modal
|
||||||
showRestartModal();
|
showRestartModal();
|
||||||
return;
|
return;
|
||||||
|
@ -327,6 +375,12 @@ $(document).ready(function(){
|
||||||
$('#' + UPLOAD_CONTENT_ALLOWED_DIV_ID).toggle(!data.status.isRecovering);
|
$('#' + UPLOAD_CONTENT_ALLOWED_DIV_ID).toggle(!data.status.isRecovering);
|
||||||
$('#' + UPLOAD_CONTENT_RECOVERING_DIV_ID).toggle(data.status.isRecovering);
|
$('#' + UPLOAD_CONTENT_RECOVERING_DIV_ID).toggle(data.status.isRecovering);
|
||||||
|
|
||||||
|
$('#' + INSTALLED_CONTENT_NAME_ID).text(data.installed_content.name);
|
||||||
|
$('#' + INSTALLED_CONTENT_FILENAME_ID).text(data.installed_content.filename);
|
||||||
|
$('#' + INSTALLED_CONTENT_CREATED_ID).text(data.installed_content.creation_time ? moment(data.installed_content.creation_time).format('lll') : "");
|
||||||
|
$('#' + INSTALLED_CONTENT_INSTALLED_ID).text(data.installed_content.install_time ? moment(data.installed_content.install_time).format('lll') : "");
|
||||||
|
//$('#' + INSTALLED_CONTENT_INSTALLED_BY_ID).text(data.installed_content.installed_by);
|
||||||
|
|
||||||
// update the progress bars for current restore status
|
// update the progress bars for current restore status
|
||||||
if (data.status.isRecovering) {
|
if (data.status.isRecovering) {
|
||||||
updateProgressBars($('.recovery.progress-bar'), data.status.recoveryProgress * 100);
|
updateProgressBars($('.recovery.progress-bar'), data.status.recoveryProgress * 100);
|
||||||
|
@ -514,6 +568,7 @@ $(document).ready(function(){
|
||||||
Settings.afterReloadActions = function() {
|
Settings.afterReloadActions = function() {
|
||||||
setupBackupUpload();
|
setupBackupUpload();
|
||||||
setupContentArchives();
|
setupContentArchives();
|
||||||
|
setupInstalledContentInfo();
|
||||||
|
|
||||||
// load the latest backups immediately
|
// load the latest backups immediately
|
||||||
reloadBackupInformation();
|
reloadBackupInformation();
|
||||||
|
|
|
@ -57,10 +57,14 @@ $(document).ready(function(){
|
||||||
// define extra groups to add to setting panels, with their splice index
|
// define extra groups to add to setting panels, with their splice index
|
||||||
Settings.extraContentGroupsAtIndex = {
|
Settings.extraContentGroupsAtIndex = {
|
||||||
0: {
|
0: {
|
||||||
|
html_id: Settings.INSTALLED_CONTENT,
|
||||||
|
label: 'Installed Content'
|
||||||
|
},
|
||||||
|
1: {
|
||||||
html_id: Settings.CONTENT_ARCHIVES_PANEL_ID,
|
html_id: Settings.CONTENT_ARCHIVES_PANEL_ID,
|
||||||
label: 'Content Archives'
|
label: 'Content Archives'
|
||||||
},
|
},
|
||||||
1: {
|
2: {
|
||||||
html_id: Settings.UPLOAD_CONTENT_BACKUP_PANEL_ID,
|
html_id: Settings.UPLOAD_CONTENT_BACKUP_PANEL_ID,
|
||||||
label: 'Upload Content'
|
label: 'Upload Content'
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,8 @@ $.extend(Settings, {
|
||||||
INVALID_ROW_CLASS: 'invalid-input',
|
INVALID_ROW_CLASS: 'invalid-input',
|
||||||
DATA_ROW_INDEX: 'data-row-index',
|
DATA_ROW_INDEX: 'data-row-index',
|
||||||
CONTENT_ARCHIVES_PANEL_ID: 'content_archives',
|
CONTENT_ARCHIVES_PANEL_ID: 'content_archives',
|
||||||
UPLOAD_CONTENT_BACKUP_PANEL_ID: 'upload_content'
|
UPLOAD_CONTENT_BACKUP_PANEL_ID: 'upload_content',
|
||||||
|
INSTALLED_CONTENT: 'installed_content'
|
||||||
});
|
});
|
||||||
|
|
||||||
var URLs = {
|
var URLs = {
|
||||||
|
|
|
@ -278,17 +278,19 @@ void AssetsBackupHandler::createBackup(const QString& backupName, QuaZip& zip) {
|
||||||
_backups.emplace_back(backupName, mappings, false);
|
_backups.emplace_back(backupName, mappings, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip) {
|
std::pair<bool, QString> AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
if (operationInProgress()) {
|
if (operationInProgress()) {
|
||||||
qCWarning(asset_backup) << "There is already a backup/restore in progress.";
|
QString errorStr ("There is already a backup/restore in progress. Please wait.");
|
||||||
return;
|
qWarning() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_lastMappingsRefresh.time_since_epoch().count() == 0) {
|
if (_lastMappingsRefresh.time_since_epoch().count() == 0) {
|
||||||
qCWarning(asset_backup) << "Current mappings not yet loaded.";
|
QString errorStr ("Current mappings not yet loaded. Please wait.");
|
||||||
return;
|
qWarning() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((p_high_resolution_clock::now() - _lastMappingsRefresh) > MAX_REFRESH_TIME) {
|
if ((p_high_resolution_clock::now() - _lastMappingsRefresh) > MAX_REFRESH_TIME) {
|
||||||
|
@ -301,6 +303,16 @@ void AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip)
|
||||||
if (it == end(_backups)) {
|
if (it == end(_backups)) {
|
||||||
loadBackup(backupName, zip);
|
loadBackup(backupName, zip);
|
||||||
|
|
||||||
|
auto emplaced_backup = find_if(begin(_backups), end(_backups), [&](const AssetServerBackup& backup) {
|
||||||
|
return backup.name == backupName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(emplaced_backup->corruptedBackup) {
|
||||||
|
QString errorStr ("Current mappings file is corrupted.");
|
||||||
|
qWarning() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
|
}
|
||||||
|
|
||||||
QuaZipDir zipDir { &zip, ZIP_ASSETS_FOLDER };
|
QuaZipDir zipDir { &zip, ZIP_ASSETS_FOLDER };
|
||||||
|
|
||||||
auto assetNames = zipDir.entryList(QDir::Files);
|
auto assetNames = zipDir.entryList(QDir::Files);
|
||||||
|
@ -330,8 +342,9 @@ void AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (it == end(_backups)) {
|
if (it == end(_backups)) {
|
||||||
qCCritical(asset_backup) << "Failed to recover backup:" << backupName;
|
QString errorStr ("Failed to recover backup: " + backupName);
|
||||||
return;
|
qWarning() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,6 +352,7 @@ void AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip)
|
||||||
computeServerStateDifference(_currentMappings, newMappings);
|
computeServerStateDifference(_currentMappings, newMappings);
|
||||||
|
|
||||||
restoreAllAssets();
|
restoreAllAssets();
|
||||||
|
return { true, QString() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsBackupHandler::deleteBackup(const QString& backupName) {
|
void AssetsBackupHandler::deleteBackup(const QString& backupName) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ public:
|
||||||
void loadBackup(const QString& backupName, QuaZip& zip) override;
|
void loadBackup(const QString& backupName, QuaZip& zip) override;
|
||||||
void loadingComplete() override;
|
void loadingComplete() override;
|
||||||
void createBackup(const QString& backupName, QuaZip& zip) override;
|
void createBackup(const QString& backupName, QuaZip& zip) override;
|
||||||
void recoverBackup(const QString& backupName, QuaZip& zip) override;
|
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) override;
|
||||||
void deleteBackup(const QString& backupName) override;
|
void deleteBackup(const QString& backupName) override;
|
||||||
void consolidateBackup(const QString& backupName, QuaZip& zip) override;
|
void consolidateBackup(const QString& backupName, QuaZip& zip) override;
|
||||||
bool isCorruptedBackup(const QString& backupName) override;
|
bool isCorruptedBackup(const QString& backupName) override;
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
virtual void loadBackup(const QString& backupName, QuaZip& zip) = 0;
|
virtual void loadBackup(const QString& backupName, QuaZip& zip) = 0;
|
||||||
virtual void loadingComplete() = 0;
|
virtual void loadingComplete() = 0;
|
||||||
virtual void createBackup(const QString& backupName, QuaZip& zip) = 0;
|
virtual void createBackup(const QString& backupName, QuaZip& zip) = 0;
|
||||||
virtual void recoverBackup(const QString& backupName, QuaZip& zip) = 0;
|
virtual std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) = 0;
|
||||||
virtual void deleteBackup(const QString& backupName) = 0;
|
virtual void deleteBackup(const QString& backupName) = 0;
|
||||||
virtual void consolidateBackup(const QString& backupName, QuaZip& zip) = 0;
|
virtual void consolidateBackup(const QString& backupName, QuaZip& zip) = 0;
|
||||||
virtual bool isCorruptedBackup(const QString& backupName) = 0;
|
virtual bool isCorruptedBackup(const QString& backupName) = 0;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "ContentSettingsBackupHandler.h"
|
#include "ContentSettingsBackupHandler.h"
|
||||||
|
#include "DomainContentBackupManager.h"
|
||||||
|
|
||||||
#if !defined(__clang__) && defined(__GNUC__)
|
#if !defined(__clang__) && defined(__GNUC__)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const QString DATETIME_FORMAT { "yyyy-MM-dd_HH-mm-ss" };
|
||||||
|
|
||||||
ContentSettingsBackupHandler::ContentSettingsBackupHandler(DomainServerSettingsManager& domainServerSettingsManager) :
|
ContentSettingsBackupHandler::ContentSettingsBackupHandler(DomainServerSettingsManager& domainServerSettingsManager) :
|
||||||
_settingsManager(domainServerSettingsManager)
|
_settingsManager(domainServerSettingsManager)
|
||||||
|
@ -41,6 +43,26 @@ void ContentSettingsBackupHandler::createBackup(const QString& backupName, QuaZi
|
||||||
DomainServerSettingsManager::IncludeContentSettings, DomainServerSettingsManager::NoDefaultSettings,
|
DomainServerSettingsManager::IncludeContentSettings, DomainServerSettingsManager::NoDefaultSettings,
|
||||||
DomainServerSettingsManager::ForBackup
|
DomainServerSettingsManager::ForBackup
|
||||||
);
|
);
|
||||||
|
QString prefixFormat = "(" + QRegExp::escape(AUTOMATIC_BACKUP_PREFIX) + "|" + QRegExp::escape(MANUAL_BACKUP_PREFIX) + ")";
|
||||||
|
QString nameFormat = "(.+)";
|
||||||
|
QString dateTimeFormat = "(" + DATETIME_FORMAT_RE + ")";
|
||||||
|
QRegExp backupNameFormat { prefixFormat + nameFormat + "-" + dateTimeFormat + "\\.zip" };
|
||||||
|
|
||||||
|
QString name{ "" };
|
||||||
|
QDateTime createdAt;
|
||||||
|
|
||||||
|
if (backupNameFormat.exactMatch(backupName)) {
|
||||||
|
name = backupNameFormat.cap(2);
|
||||||
|
auto dateTime = backupNameFormat.cap(3);
|
||||||
|
createdAt = QDateTime::fromString(dateTime, DATETIME_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject installed_content {
|
||||||
|
{ INSTALLED_CONTENT_NAME, name},
|
||||||
|
{ INSTALLED_CONTENT_CREATION_TIME, createdAt.currentMSecsSinceEpoch()}
|
||||||
|
};
|
||||||
|
|
||||||
|
contentSettingsJSON.insert(INSTALLED_CONTENT, installed_content);
|
||||||
|
|
||||||
// make a QJsonDocument using the object
|
// make a QJsonDocument using the object
|
||||||
QJsonDocument contentSettingsDocument { contentSettingsJSON };
|
QJsonDocument contentSettingsDocument { contentSettingsJSON };
|
||||||
|
@ -62,24 +84,48 @@ void ContentSettingsBackupHandler::createBackup(const QString& backupName, QuaZi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentSettingsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip) {
|
std::pair<bool, QString> ContentSettingsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) {
|
||||||
if (!zip.setCurrentFile(CONTENT_SETTINGS_BACKUP_FILENAME)) {
|
if (!zip.setCurrentFile(CONTENT_SETTINGS_BACKUP_FILENAME)) {
|
||||||
qWarning() << "Failed to find" << CONTENT_SETTINGS_BACKUP_FILENAME << "while recovering backup";
|
QString errorStr("Failed to find " + CONTENT_SETTINGS_BACKUP_FILENAME + " while recovering backup");
|
||||||
return;
|
qWarning() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
|
|
||||||
QuaZipFile zipFile { &zip };
|
QuaZipFile zipFile { &zip };
|
||||||
if (!zipFile.open(QIODevice::ReadOnly)) {
|
if (!zipFile.open(QIODevice::ReadOnly)) {
|
||||||
qCritical() << "Failed to open" << CONTENT_SETTINGS_BACKUP_FILENAME << "in backup";
|
QString errorStr("Failed to open " + CONTENT_SETTINGS_BACKUP_FILENAME + " in backup");
|
||||||
return;
|
qCritical() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rawData = zipFile.readAll();
|
auto rawData = zipFile.readAll();
|
||||||
zipFile.close();
|
zipFile.close();
|
||||||
|
|
||||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(rawData);
|
if (zipFile.getZipError() != UNZ_OK) {
|
||||||
|
QString errorStr("Failed to unzip " + CONTENT_SETTINGS_BACKUP_FILENAME + ": " + zipFile.getZipError());
|
||||||
if (!_settingsManager.restoreSettingsFromObject(jsonDocument.object(), ContentSettings)) {
|
qCritical() << errorStr;
|
||||||
qCritical() << "Failed to restore settings from" << CONTENT_SETTINGS_BACKUP_FILENAME << "in content archive";
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonDocument jsonDocument = QJsonDocument::fromJson(rawData);
|
||||||
|
QJsonObject jsonObject = jsonDocument.object();
|
||||||
|
|
||||||
|
auto archiveJson = jsonObject.find(INSTALLED_CONTENT)->toObject();
|
||||||
|
|
||||||
|
QJsonObject installed_content {
|
||||||
|
{ INSTALLED_CONTENT_FILENAME, sourceFilename },
|
||||||
|
{ INSTALLED_CONTENT_NAME, archiveJson[INSTALLED_CONTENT_NAME].toString()},
|
||||||
|
{ INSTALLED_CONTENT_CREATION_TIME, archiveJson[INSTALLED_CONTENT_CREATION_TIME].toVariant().toLongLong() },
|
||||||
|
{ INSTALLED_CONTENT_INSTALL_TIME, QDateTime::currentDateTime().currentMSecsSinceEpoch() },
|
||||||
|
{ INSTALLED_CONTENT_INSTALLED_BY, "" }
|
||||||
|
};
|
||||||
|
|
||||||
|
jsonObject.insert(INSTALLED_CONTENT, installed_content);
|
||||||
|
|
||||||
|
if (!_settingsManager.restoreSettingsFromObject(jsonObject, ContentSettings)) {
|
||||||
|
QString errorStr("Failed to restore settings from " + CONTENT_SETTINGS_BACKUP_FILENAME + " in content archive");
|
||||||
|
qCritical() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
|
}
|
||||||
|
return { true, QString() };
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
|
|
||||||
void createBackup(const QString& backupName, QuaZip& zip) override;
|
void createBackup(const QString& backupName, QuaZip& zip) override;
|
||||||
|
|
||||||
void recoverBackup(const QString& backupName, QuaZip& zip) override;
|
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) override;
|
||||||
|
|
||||||
void deleteBackup(const QString& backupName) override {}
|
void deleteBackup(const QString& backupName) override {}
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,7 @@ const std::chrono::seconds DomainContentBackupManager::DEFAULT_PERSIST_INTERVAL
|
||||||
|
|
||||||
// Backup format looks like: daily_backup-TIMESTAMP.zip
|
// Backup format looks like: daily_backup-TIMESTAMP.zip
|
||||||
static const QString DATETIME_FORMAT { "yyyy-MM-dd_HH-mm-ss" };
|
static const QString DATETIME_FORMAT { "yyyy-MM-dd_HH-mm-ss" };
|
||||||
static const QString DATETIME_FORMAT_RE { "\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}" };
|
static const QString PRE_UPLOAD_SUFFIX{ "pre_upload" };
|
||||||
static const QString AUTOMATIC_BACKUP_PREFIX { "autobackup-" };
|
|
||||||
static const QString MANUAL_BACKUP_PREFIX { "backup-" };
|
|
||||||
static const QString MANUAL_BACKUP_NAME_RE { "[a-zA-Z0-9\\-_ ]+" };
|
static const QString MANUAL_BACKUP_NAME_RE { "[a-zA-Z0-9\\-_ ]+" };
|
||||||
|
|
||||||
void DomainContentBackupManager::addBackupHandler(BackupHandlerPointer handler) {
|
void DomainContentBackupManager::addBackupHandler(BackupHandlerPointer handler) {
|
||||||
|
@ -52,9 +50,10 @@ void DomainContentBackupManager::addBackupHandler(BackupHandlerPointer handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainContentBackupManager::DomainContentBackupManager(const QString& backupDirectory,
|
DomainContentBackupManager::DomainContentBackupManager(const QString& backupDirectory,
|
||||||
const QVariantList& backupRules,
|
DomainServerSettingsManager& domainServerSettingsManager,
|
||||||
std::chrono::milliseconds persistInterval,
|
std::chrono::milliseconds persistInterval,
|
||||||
bool debugTimestampNow) :
|
bool debugTimestampNow) :
|
||||||
|
_settingsManager(domainServerSettingsManager),
|
||||||
_consolidatedBackupDirectory(PathUtils::generateTemporaryDir()),
|
_consolidatedBackupDirectory(PathUtils::generateTemporaryDir()),
|
||||||
_backupDirectory(backupDirectory), _persistInterval(persistInterval), _lastCheck(p_high_resolution_clock::now())
|
_backupDirectory(backupDirectory), _persistInterval(persistInterval), _lastCheck(p_high_resolution_clock::now())
|
||||||
{
|
{
|
||||||
|
@ -63,7 +62,8 @@ DomainContentBackupManager::DomainContentBackupManager(const QString& backupDire
|
||||||
// Make sure the backup directory exists.
|
// Make sure the backup directory exists.
|
||||||
QDir(_backupDirectory).mkpath(".");
|
QDir(_backupDirectory).mkpath(".");
|
||||||
|
|
||||||
parseBackupRules(backupRules);
|
static const QString BACKUP_RULES_KEYPATH = AUTOMATIC_CONTENT_ARCHIVES_GROUP + ".backup_rules";
|
||||||
|
parseBackupRules(_settingsManager.valueOrDefaultValueForKeyPath(BACKUP_RULES_KEYPATH).toList());
|
||||||
|
|
||||||
constexpr int CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS = 30 * 1000;
|
constexpr int CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS = 30 * 1000;
|
||||||
_consolidatedBackupCleanupTimer.setInterval(CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS);
|
_consolidatedBackupCleanupTimer.setInterval(CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS);
|
||||||
|
@ -170,7 +170,9 @@ bool DomainContentBackupManager::process() {
|
||||||
return handler->getRecoveryStatus().first;
|
return handler->getRecoveryStatus().first;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isStillRecovering) {
|
// if an error occurred, don't restart the server so that the user
|
||||||
|
// can be notified of the error and take action.
|
||||||
|
if (!isStillRecovering && _recoveryError.isEmpty()) {
|
||||||
_isRecovering = false;
|
_isRecovering = false;
|
||||||
_recoveryFilename = "";
|
_recoveryFilename = "";
|
||||||
emit recoveryCompleted();
|
emit recoveryCompleted();
|
||||||
|
@ -277,7 +279,7 @@ void DomainContentBackupManager::deleteBackup(MiniPromise::Promise promise, cons
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomainContentBackupManager::recoverFromBackupZip(const QString& backupName, QuaZip& zip) {
|
bool DomainContentBackupManager::recoverFromBackupZip(const QString& backupName, QuaZip& zip, const QString& sourceFilename, bool rollingBack) {
|
||||||
if (!zip.open(QuaZip::Mode::mdUnzip)) {
|
if (!zip.open(QuaZip::Mode::mdUnzip)) {
|
||||||
qWarning() << "Failed to unzip file: " << backupName;
|
qWarning() << "Failed to unzip file: " << backupName;
|
||||||
return false;
|
return false;
|
||||||
|
@ -286,7 +288,15 @@ bool DomainContentBackupManager::recoverFromBackupZip(const QString& backupName,
|
||||||
_recoveryFilename = backupName;
|
_recoveryFilename = backupName;
|
||||||
|
|
||||||
for (auto& handler : _backupHandlers) {
|
for (auto& handler : _backupHandlers) {
|
||||||
handler->recoverBackup(backupName, zip);
|
bool success;
|
||||||
|
QString errorStr;
|
||||||
|
std::tie(success, errorStr) = handler->recoverBackup(backupName, zip, sourceFilename);
|
||||||
|
if (!success) {
|
||||||
|
if (!rollingBack) {
|
||||||
|
_recoveryError = errorStr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Successfully started recovering from " << backupName;
|
qDebug() << "Successfully started recovering from " << backupName;
|
||||||
|
@ -309,7 +319,7 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Recovering from" << backupName;
|
qDebug() << "Recovering from" << backupName;
|
||||||
|
_recoveryError = "";
|
||||||
bool success { false };
|
bool success { false };
|
||||||
QDir backupDir { _backupDirectory };
|
QDir backupDir { _backupDirectory };
|
||||||
auto backupFilePath { backupDir.filePath(backupName) };
|
auto backupFilePath { backupDir.filePath(backupName) };
|
||||||
|
@ -317,7 +327,7 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
|
||||||
if (backupFile.open(QIODevice::ReadOnly)) {
|
if (backupFile.open(QIODevice::ReadOnly)) {
|
||||||
QuaZip zip { &backupFile };
|
QuaZip zip { &backupFile };
|
||||||
|
|
||||||
success = recoverFromBackupZip(backupName, zip);
|
success = recoverFromBackupZip(backupName, zip, backupName);
|
||||||
|
|
||||||
backupFile.close();
|
backupFile.close();
|
||||||
} else {
|
} else {
|
||||||
|
@ -345,29 +355,51 @@ void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise
|
||||||
QuaZip uploadedZip { &uploadedBackupBuffer };
|
QuaZip uploadedZip { &uploadedBackupBuffer };
|
||||||
|
|
||||||
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
||||||
bool success = recoverFromBackupZip(backupName, uploadedZip);
|
bool success = recoverFromBackupZip(backupName, uploadedZip, QString());
|
||||||
|
|
||||||
promise->resolve({
|
promise->resolve({
|
||||||
{ "success", success }
|
{ "success", success }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename) {
|
void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename, QString sourceFilename) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "recoverFromUploadedFile", Q_ARG(MiniPromise::Promise, promise),
|
QMetaObject::invokeMethod(this, "recoverFromUploadedFile", Q_ARG(MiniPromise::Promise, promise),
|
||||||
Q_ARG(QString, uploadedFilename));
|
Q_ARG(QString, uploadedFilename), Q_ARG(QString, sourceFilename));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Recovering from uploaded file -" << uploadedFilename;
|
qDebug() << "Recovering from uploaded file -" << uploadedFilename << "source" << sourceFilename;
|
||||||
|
bool success;
|
||||||
|
QString path;
|
||||||
|
std::tie(success, path) = createBackup(AUTOMATIC_BACKUP_PREFIX, PRE_UPLOAD_SUFFIX);
|
||||||
|
if(!success) {
|
||||||
|
_recoveryError = "Failed to create backup for " + PRE_UPLOAD_SUFFIX + " at " + path;
|
||||||
|
qCWarning(domain_server) << _recoveryError;
|
||||||
|
} else {
|
||||||
|
QFile uploadedFile(uploadedFilename);
|
||||||
|
QuaZip uploadedZip { &uploadedFile };
|
||||||
|
|
||||||
QFile uploadedFile(uploadedFilename);
|
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
||||||
QuaZip uploadedZip { &uploadedFile };
|
|
||||||
|
|
||||||
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
bool success = recoverFromBackupZip(backupName, uploadedZip, sourceFilename);
|
||||||
|
|
||||||
bool success = recoverFromBackupZip(backupName, uploadedZip);
|
if (!success) {
|
||||||
|
|
||||||
|
// attempt to rollback to
|
||||||
|
QString filename;
|
||||||
|
QDateTime filetime;
|
||||||
|
if (getMostRecentBackup(PRE_UPLOAD_SUFFIX, filename, filetime)) {
|
||||||
|
QFile uploadedFile(uploadedFilename);
|
||||||
|
QuaZip uploadedZip { &uploadedFile };
|
||||||
|
|
||||||
|
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
|
||||||
|
recoverFromBackupZip(backupName, uploadedZip, sourceFilename, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
promise->resolve({
|
promise->resolve({
|
||||||
{ "success", success }
|
{ "success", success }
|
||||||
});
|
});
|
||||||
|
@ -455,9 +487,44 @@ void DomainContentBackupManager::getAllBackupsAndStatus(MiniPromise::Promise pro
|
||||||
{ "recoveryProgress", recoveryProgress }
|
{ "recoveryProgress", recoveryProgress }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(!_recoveryError.isEmpty()) {
|
||||||
|
status["recoveryError"] = _recoveryError;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString filename = _settingsManager.valueForKeyPath(CONTENT_SETTINGS_INSTALLED_CONTENT_FILENAME).toString();
|
||||||
|
QString name = _settingsManager.valueForKeyPath(CONTENT_SETTINGS_INSTALLED_CONTENT_NAME).toString();
|
||||||
|
auto creationTime = _settingsManager.valueForKeyPath(CONTENT_SETTINGS_INSTALLED_CONTENT_CREATION_TIME).toULongLong();
|
||||||
|
|
||||||
|
if (name.isEmpty() || creationTime == 0) {
|
||||||
|
QString prefixFormat = "(" + QRegExp::escape(AUTOMATIC_BACKUP_PREFIX) + "|" + QRegExp::escape(MANUAL_BACKUP_PREFIX) + ")";
|
||||||
|
QString nameFormat = "(.+)";
|
||||||
|
QString dateTimeFormat = "(" + DATETIME_FORMAT_RE + ")";
|
||||||
|
QRegExp backupNameFormat { prefixFormat + nameFormat + "-" + dateTimeFormat + "\\.zip" };
|
||||||
|
|
||||||
|
|
||||||
|
if (backupNameFormat.exactMatch(filename)) {
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = backupNameFormat.cap(2);
|
||||||
|
}
|
||||||
|
if (creationTime == 0) {
|
||||||
|
auto dateTime = backupNameFormat.cap(3);
|
||||||
|
creationTime = QDateTime::fromString(dateTime, DATETIME_FORMAT).toMSecsSinceEpoch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap currentArchive;
|
||||||
|
currentArchive["filename"] = filename;
|
||||||
|
currentArchive["name"] = name;
|
||||||
|
currentArchive["creation_time"] = creationTime;
|
||||||
|
currentArchive["install_time"] = _settingsManager.valueForKeyPath(CONTENT_SETTINGS_INSTALLED_CONTENT_INSTALL_TIME).toULongLong();
|
||||||
|
currentArchive["installed_by"] = _settingsManager.valueForKeyPath(CONTENT_SETTINGS_INSTALLED_CONTENT_INSTALLED_BY).toString();
|
||||||
|
|
||||||
QVariantMap info {
|
QVariantMap info {
|
||||||
{ "backups", variantBackups },
|
{ "backups", variantBackups },
|
||||||
{ "status", status }
|
{ "status", status },
|
||||||
|
{ "installed_content", currentArchive }
|
||||||
};
|
};
|
||||||
|
|
||||||
promise->resolve(info);
|
promise->resolve(info);
|
||||||
|
|
|
@ -28,11 +28,22 @@
|
||||||
#include <GenericThread.h>
|
#include <GenericThread.h>
|
||||||
|
|
||||||
#include "BackupHandler.h"
|
#include "BackupHandler.h"
|
||||||
|
#include "DomainServerSettingsManager.h"
|
||||||
|
|
||||||
#include <shared/MiniPromises.h>
|
#include <shared/MiniPromises.h>
|
||||||
|
|
||||||
#include <PortableHighResolutionClock.h>
|
#include <PortableHighResolutionClock.h>
|
||||||
|
|
||||||
|
const QString DATETIME_FORMAT_RE { "\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}" };
|
||||||
|
const QString AUTOMATIC_BACKUP_PREFIX { "autobackup-" };
|
||||||
|
const QString MANUAL_BACKUP_PREFIX { "backup-" };
|
||||||
|
const QString INSTALLED_CONTENT = "installed_content";
|
||||||
|
const QString INSTALLED_CONTENT_FILENAME = "filename";
|
||||||
|
const QString INSTALLED_CONTENT_NAME = "name";
|
||||||
|
const QString INSTALLED_CONTENT_CREATION_TIME = "creation_time";
|
||||||
|
const QString INSTALLED_CONTENT_INSTALL_TIME = "install_time";
|
||||||
|
const QString INSTALLED_CONTENT_INSTALLED_BY = "installed_by";
|
||||||
|
|
||||||
struct BackupItemInfo {
|
struct BackupItemInfo {
|
||||||
BackupItemInfo(QString pId, QString pName, QString pAbsolutePath, QDateTime pCreatedAt, bool pIsManualBackup) :
|
BackupItemInfo(QString pId, QString pName, QString pAbsolutePath, QDateTime pCreatedAt, bool pIsManualBackup) :
|
||||||
id(pId), name(pName), absolutePath(pAbsolutePath), createdAt(pCreatedAt), isManualBackup(pIsManualBackup) { };
|
id(pId), name(pName), absolutePath(pAbsolutePath), createdAt(pCreatedAt), isManualBackup(pIsManualBackup) { };
|
||||||
|
@ -71,7 +82,7 @@ public:
|
||||||
static const std::chrono::seconds DEFAULT_PERSIST_INTERVAL;
|
static const std::chrono::seconds DEFAULT_PERSIST_INTERVAL;
|
||||||
|
|
||||||
DomainContentBackupManager(const QString& rootBackupDirectory,
|
DomainContentBackupManager(const QString& rootBackupDirectory,
|
||||||
const QVariantList& settings,
|
DomainServerSettingsManager& domainServerSettingsManager,
|
||||||
std::chrono::milliseconds persistInterval = DEFAULT_PERSIST_INTERVAL,
|
std::chrono::milliseconds persistInterval = DEFAULT_PERSIST_INTERVAL,
|
||||||
bool debugTimestampNow = false);
|
bool debugTimestampNow = false);
|
||||||
|
|
||||||
|
@ -86,7 +97,7 @@ public slots:
|
||||||
void createManualBackup(MiniPromise::Promise promise, const QString& name);
|
void createManualBackup(MiniPromise::Promise promise, const QString& name);
|
||||||
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName);
|
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName);
|
||||||
void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup);
|
void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup);
|
||||||
void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename);
|
void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename, QString sourceFilename);
|
||||||
void deleteBackup(MiniPromise::Promise promise, const QString& backupName);
|
void deleteBackup(MiniPromise::Promise promise, const QString& backupName);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -108,13 +119,15 @@ protected:
|
||||||
|
|
||||||
std::pair<bool, QString> createBackup(const QString& prefix, const QString& name);
|
std::pair<bool, QString> createBackup(const QString& prefix, const QString& name);
|
||||||
|
|
||||||
bool recoverFromBackupZip(const QString& backupName, QuaZip& backupZip);
|
bool recoverFromBackupZip(const QString& backupName, QuaZip& backupZip, const QString& sourceFilename, bool rollingBack = false);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void removeOldConsolidatedBackups();
|
void removeOldConsolidatedBackups();
|
||||||
void consolidateBackupInternal(QString fileName);
|
void consolidateBackupInternal(QString fileName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
DomainServerSettingsManager& _settingsManager;
|
||||||
|
|
||||||
QTimer _consolidatedBackupCleanupTimer;
|
QTimer _consolidatedBackupCleanupTimer;
|
||||||
|
|
||||||
const QString _consolidatedBackupDirectory;
|
const QString _consolidatedBackupDirectory;
|
||||||
|
@ -126,6 +139,7 @@ private:
|
||||||
std::unordered_map<QString, ConsolidatedBackupInfo> _consolidatedBackups;
|
std::unordered_map<QString, ConsolidatedBackupInfo> _consolidatedBackups;
|
||||||
|
|
||||||
std::atomic<bool> _isRecovering { false };
|
std::atomic<bool> _isRecovering { false };
|
||||||
|
QString _recoveryError;
|
||||||
QString _recoveryFilename { };
|
QString _recoveryFilename { };
|
||||||
|
|
||||||
p_high_resolution_clock::time_point _lastCheck;
|
p_high_resolution_clock::time_point _lastCheck;
|
||||||
|
|
|
@ -307,11 +307,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
}
|
}
|
||||||
maybeHandleReplacementEntityFile();
|
maybeHandleReplacementEntityFile();
|
||||||
|
|
||||||
|
_contentManager.reset(new DomainContentBackupManager(getContentBackupDir(), _settingsManager));
|
||||||
static const QString BACKUP_RULES_KEYPATH = AUTOMATIC_CONTENT_ARCHIVES_GROUP + ".backup_rules";
|
|
||||||
auto backupRulesVariant = _settingsManager.valueOrDefaultValueForKeyPath(BACKUP_RULES_KEYPATH);
|
|
||||||
|
|
||||||
_contentManager.reset(new DomainContentBackupManager(getContentBackupDir(), backupRulesVariant.toList()));
|
|
||||||
|
|
||||||
connect(_contentManager.get(), &DomainContentBackupManager::started, _contentManager.get(), [this](){
|
connect(_contentManager.get(), &DomainContentBackupManager::started, _contentManager.get(), [this](){
|
||||||
_contentManager->addBackupHandler(BackupHandlerPointer(new EntitiesBackupHandler(getEntitiesFilePath(), getEntitiesReplacementFilePath())));
|
_contentManager->addBackupHandler(BackupHandlerPointer(new EntitiesBackupHandler(getEntitiesFilePath(), getEntitiesReplacementFilePath())));
|
||||||
|
@ -2194,7 +2190,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if (url.path() == URI_RESTART) {
|
} else if (url.path() == URI_RESTART) {
|
||||||
connection->respond(HTTPConnection::StatusCode200);
|
connection->respond(HTTPConnection::StatusCode204);
|
||||||
restart();
|
restart();
|
||||||
return true;
|
return true;
|
||||||
} else if (url.path() == URI_API_METAVERSE_INFO) {
|
} else if (url.path() == URI_API_METAVERSE_INFO) {
|
||||||
|
@ -2333,8 +2329,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
QJsonObject rootJSON;
|
QJsonObject rootJSON;
|
||||||
auto success = result["success"].toBool();
|
auto success = result["success"].toBool();
|
||||||
rootJSON["success"] = success;
|
QJsonDocument docJSON(QJsonObject::fromVariantMap(result));
|
||||||
QJsonDocument docJSON(rootJSON);
|
|
||||||
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
||||||
JSON_MIME_TYPE.toUtf8());
|
JSON_MIME_TYPE.toUtf8());
|
||||||
});
|
});
|
||||||
|
@ -2362,8 +2357,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
QJsonObject rootJSON;
|
QJsonObject rootJSON;
|
||||||
auto success = result["success"].toBool();
|
auto success = result["success"].toBool();
|
||||||
rootJSON["success"] = success;
|
QJsonDocument docJSON(QJsonObject::fromVariantMap(result));
|
||||||
QJsonDocument docJSON(rootJSON);
|
|
||||||
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
||||||
JSON_MIME_TYPE.toUtf8());
|
JSON_MIME_TYPE.toUtf8());
|
||||||
});
|
});
|
||||||
|
@ -2467,8 +2461,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
QJsonObject rootJSON;
|
QJsonObject rootJSON;
|
||||||
auto success = result["success"].toBool();
|
auto success = result["success"].toBool();
|
||||||
rootJSON["success"] = success;
|
QJsonDocument docJSON(QJsonObject::fromVariantMap(result));
|
||||||
QJsonDocument docJSON(rootJSON);
|
|
||||||
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
|
||||||
JSON_MIME_TYPE.toUtf8());
|
JSON_MIME_TYPE.toUtf8());
|
||||||
});
|
});
|
||||||
|
@ -2590,7 +2583,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite
|
||||||
_pendingFileContent.close();
|
_pendingFileContent.close();
|
||||||
|
|
||||||
// Respond immediately - will timeout if we wait for restore.
|
// Respond immediately - will timeout if we wait for restore.
|
||||||
connection->respond(HTTPConnection::StatusCode200);
|
connection->respond(HTTPConnection::StatusCode204);
|
||||||
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
|
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
|
||||||
auto deferred = makePromise("recoverFromUploadedBackup");
|
auto deferred = makePromise("recoverFromUploadedBackup");
|
||||||
|
|
||||||
|
@ -2598,7 +2591,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite
|
||||||
_pendingContentFiles.erase(sessionId);
|
_pendingContentFiles.erase(sessionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
_contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName());
|
_contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName(), filename);
|
||||||
}
|
}
|
||||||
} else if (filename.endsWith(".json", Qt::CaseInsensitive)
|
} else if (filename.endsWith(".json", Qt::CaseInsensitive)
|
||||||
|| filename.endsWith(".json.gz", Qt::CaseInsensitive)) {
|
|| filename.endsWith(".json.gz", Qt::CaseInsensitive)) {
|
||||||
|
@ -2608,14 +2601,16 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite
|
||||||
}
|
}
|
||||||
QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId];
|
QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId];
|
||||||
_pendingUploadedContent += dataChunk;
|
_pendingUploadedContent += dataChunk;
|
||||||
connection->respond(HTTPConnection::StatusCode200);
|
|
||||||
|
|
||||||
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
|
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
|
||||||
// invoke our method to hand the new octree file off to the octree server
|
// invoke our method to hand the new octree file off to the octree server
|
||||||
QMetaObject::invokeMethod(this, "handleOctreeFileReplacement",
|
if (!handleOctreeFileReplacement(_pendingUploadedContent, filename, QString())) {
|
||||||
Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent));
|
connection->respond(HTTPConnection::StatusCode400);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
_pendingUploadedContents.erase(sessionId);
|
_pendingUploadedContents.erase(sessionId);
|
||||||
}
|
}
|
||||||
|
connection->respond(HTTPConnection::StatusCode204);
|
||||||
} else {
|
} else {
|
||||||
connection->respond(HTTPConnection::StatusCode400);
|
connection->respond(HTTPConnection::StatusCode400);
|
||||||
return false;
|
return false;
|
||||||
|
@ -2687,11 +2682,12 @@ void DomainServer::profileRequestFinished() {
|
||||||
|
|
||||||
bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url) {
|
bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url) {
|
||||||
|
|
||||||
const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
|
static const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
|
||||||
const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
|
static const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
|
||||||
const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles";
|
static const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles";
|
||||||
const QString BASIC_AUTH_USERNAME_KEY_PATH = "security.http_username";
|
static const QString BASIC_AUTH_USERNAME_KEY_PATH = "security.http_username";
|
||||||
const QString BASIC_AUTH_PASSWORD_KEY_PATH = "security.http_password";
|
static const QString BASIC_AUTH_PASSWORD_KEY_PATH = "security.http_password";
|
||||||
|
const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)";
|
||||||
|
|
||||||
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
|
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
|
||||||
|
|
||||||
|
@ -2702,7 +2698,6 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
&& (adminUsersVariant.isValid() || adminRolesVariant.isValid())) {
|
&& (adminUsersVariant.isValid() || adminRolesVariant.isValid())) {
|
||||||
QString cookieString = connection->requestHeader(HTTP_COOKIE_HEADER_KEY);
|
QString cookieString = connection->requestHeader(HTTP_COOKIE_HEADER_KEY);
|
||||||
|
|
||||||
const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)";
|
|
||||||
QRegExp cookieUUIDRegex(COOKIE_UUID_REGEX_STRING);
|
QRegExp cookieUUIDRegex(COOKIE_UUID_REGEX_STRING);
|
||||||
|
|
||||||
QUuid cookieUUID;
|
QUuid cookieUUID;
|
||||||
|
@ -3498,7 +3493,7 @@ void DomainServer::maybeHandleReplacementEntityFile() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) {
|
bool DomainServer::handleOctreeFileReplacement(QByteArray octreeFile, QString sourceFilename, QString name) {
|
||||||
OctreeUtils::RawEntityData data;
|
OctreeUtils::RawEntityData data;
|
||||||
if (data.readOctreeDataInfoFromData(octreeFile)) {
|
if (data.readOctreeDataInfoFromData(octreeFile)) {
|
||||||
data.resetIdAndVersion();
|
data.resetIdAndVersion();
|
||||||
|
@ -3514,15 +3509,32 @@ void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) {
|
||||||
// process it when it comes back up
|
// process it when it comes back up
|
||||||
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
|
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
|
||||||
|
|
||||||
|
QJsonObject installed_content {
|
||||||
|
{ INSTALLED_CONTENT_FILENAME, sourceFilename },
|
||||||
|
{ INSTALLED_CONTENT_NAME, name },
|
||||||
|
{ INSTALLED_CONTENT_CREATION_TIME, 0 },
|
||||||
|
{ INSTALLED_CONTENT_INSTALL_TIME, QDateTime::currentDateTime().currentMSecsSinceEpoch() },
|
||||||
|
{ INSTALLED_CONTENT_INSTALLED_BY, "" }
|
||||||
|
};
|
||||||
|
|
||||||
|
QJsonObject jsonObject { { INSTALLED_CONTENT, installed_content } };
|
||||||
|
|
||||||
|
_settingsManager.recurseJSONObjectAndOverwriteSettings(jsonObject, ContentSettings);
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "restart", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, "restart", Qt::QueuedConnection);
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Could not write replacement octree data to file - refusing to process";
|
qWarning() << "Could not write replacement octree data to file - refusing to process";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Received replacement octree file that is invalid - refusing to process";
|
qDebug() << "Received replacement octree file that is invalid - refusing to process";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const QString CONTENT_SET_NAME_QUERY_PARAM = "name";
|
||||||
|
|
||||||
void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message) {
|
void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message) {
|
||||||
qInfo() << "Received request to replace content from a url";
|
qInfo() << "Received request to replace content from a url";
|
||||||
auto node = DependencyManager::get<LimitedNodeList>()->findNodeWithAddr(message->getSenderSockAddr());
|
auto node = DependencyManager::get<LimitedNodeList>()->findNodeWithAddr(message->getSenderSockAddr());
|
||||||
|
@ -3534,13 +3546,16 @@ void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<R
|
||||||
QNetworkRequest request(modelsURL);
|
QNetworkRequest request(modelsURL);
|
||||||
QNetworkReply* reply = networkAccessManager.get(request);
|
QNetworkReply* reply = networkAccessManager.get(request);
|
||||||
|
|
||||||
qDebug() << "Downloading JSON from: " << modelsURL;
|
qDebug() << "Downloading JSON from: " << modelsURL.toString(QUrl::FullyEncoded);
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() {
|
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() {
|
||||||
QNetworkReply::NetworkError networkError = reply->error();
|
QNetworkReply::NetworkError networkError = reply->error();
|
||||||
if (networkError == QNetworkReply::NoError) {
|
if (networkError == QNetworkReply::NoError) {
|
||||||
if (modelsURL.fileName().endsWith(".json.gz")) {
|
if (modelsURL.fileName().endsWith(".json.gz")) {
|
||||||
handleOctreeFileReplacement(reply->readAll());
|
QUrlQuery urlQuery(modelsURL.query(QUrl::FullyEncoded));
|
||||||
|
|
||||||
|
QString itemName = urlQuery.queryItemValue(CONTENT_SET_NAME_QUERY_PARAM);
|
||||||
|
handleOctreeFileReplacement(reply->readAll(), modelsURL.fileName(), itemName);
|
||||||
} else if (modelsURL.fileName().endsWith(".zip")) {
|
} else if (modelsURL.fileName().endsWith(".zip")) {
|
||||||
auto deferred = makePromise("recoverFromUploadedBackup");
|
auto deferred = makePromise("recoverFromUploadedBackup");
|
||||||
_contentManager->recoverFromUploadedBackup(deferred, reply->readAll());
|
_contentManager->recoverFromUploadedBackup(deferred, reply->readAll());
|
||||||
|
@ -3555,6 +3570,6 @@ void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer<R
|
||||||
void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message) {
|
void DomainServer::handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message) {
|
||||||
auto node = DependencyManager::get<NodeList>()->nodeWithLocalID(message->getSourceID());
|
auto node = DependencyManager::get<NodeList>()->nodeWithLocalID(message->getSourceID());
|
||||||
if (node->getCanReplaceContent()) {
|
if (node->getCanReplaceContent()) {
|
||||||
handleOctreeFileReplacement(message->readAll());
|
handleOctreeFileReplacement(message->readAll(), QString(), QString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ private slots:
|
||||||
|
|
||||||
void handleDomainContentReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message);
|
void handleDomainContentReplacementFromURLRequest(QSharedPointer<ReceivedMessage> message);
|
||||||
void handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message);
|
void handleOctreeFileReplacementRequest(QSharedPointer<ReceivedMessage> message);
|
||||||
void handleOctreeFileReplacement(QByteArray octreeFile);
|
bool handleOctreeFileReplacement(QByteArray octreeFile, QString sourceFilename, QString name);
|
||||||
|
|
||||||
void processOctreeDataRequestMessage(QSharedPointer<ReceivedMessage> message);
|
void processOctreeDataRequestMessage(QSharedPointer<ReceivedMessage> message);
|
||||||
void processOctreeDataPersistMessage(QSharedPointer<ReceivedMessage> message);
|
void processOctreeDataPersistMessage(QSharedPointer<ReceivedMessage> message);
|
||||||
|
|
|
@ -35,6 +35,11 @@ const QString MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH = "security.machine_finger
|
||||||
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
|
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
|
||||||
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
|
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
|
||||||
const QString AUTOMATIC_CONTENT_ARCHIVES_GROUP = "automatic_content_archives";
|
const QString AUTOMATIC_CONTENT_ARCHIVES_GROUP = "automatic_content_archives";
|
||||||
|
const QString CONTENT_SETTINGS_INSTALLED_CONTENT_FILENAME = "installed_content.filename";
|
||||||
|
const QString CONTENT_SETTINGS_INSTALLED_CONTENT_NAME = "installed_content.name";
|
||||||
|
const QString CONTENT_SETTINGS_INSTALLED_CONTENT_CREATION_TIME = "installed_content.creation_time";
|
||||||
|
const QString CONTENT_SETTINGS_INSTALLED_CONTENT_INSTALL_TIME = "installed_content.install_time";
|
||||||
|
const QString CONTENT_SETTINGS_INSTALLED_CONTENT_INSTALLED_BY = "installed_content.installed_by";
|
||||||
|
|
||||||
using GroupByUUIDKey = QPair<QUuid, QUuid>; // groupID, rankID
|
using GroupByUUIDKey = QPair<QUuid, QUuid>; // groupID, rankID
|
||||||
|
|
||||||
|
|
|
@ -57,36 +57,41 @@ void EntitiesBackupHandler::createBackup(const QString& backupName, QuaZip& zip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitiesBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip) {
|
std::pair<bool, QString> EntitiesBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) {
|
||||||
if (!zip.setCurrentFile(ENTITIES_BACKUP_FILENAME)) {
|
if (!zip.setCurrentFile(ENTITIES_BACKUP_FILENAME)) {
|
||||||
qWarning() << "Failed to find" << ENTITIES_BACKUP_FILENAME << "while recovering backup";
|
QString errorStr("Failed to find " + ENTITIES_BACKUP_FILENAME + " while recovering backup");
|
||||||
return;
|
qWarning() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
QuaZipFile zipFile { &zip };
|
QuaZipFile zipFile { &zip };
|
||||||
if (!zipFile.open(QIODevice::ReadOnly)) {
|
if (!zipFile.open(QIODevice::ReadOnly)) {
|
||||||
qCritical() << "Failed to open" << ENTITIES_BACKUP_FILENAME << "in backup";
|
QString errorStr("Failed to open " + ENTITIES_BACKUP_FILENAME + " in backup");
|
||||||
return;
|
qCritical() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
auto rawData = zipFile.readAll();
|
auto rawData = zipFile.readAll();
|
||||||
|
|
||||||
zipFile.close();
|
zipFile.close();
|
||||||
|
|
||||||
|
if (zipFile.getZipError() != UNZ_OK) {
|
||||||
|
QString errorStr("Failed to unzip " + ENTITIES_BACKUP_FILENAME + ": " + zipFile.getZipError());
|
||||||
|
qCritical() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
|
}
|
||||||
|
|
||||||
OctreeUtils::RawEntityData data;
|
OctreeUtils::RawEntityData data;
|
||||||
if (!data.readOctreeDataInfoFromData(rawData)) {
|
if (!data.readOctreeDataInfoFromData(rawData)) {
|
||||||
qCritical() << "Unable to parse octree data during backup recovery";
|
QString errorStr("Unable to parse octree data during backup recovery");
|
||||||
return;
|
qCritical() << errorStr;
|
||||||
|
return { false, errorStr };
|
||||||
}
|
}
|
||||||
|
|
||||||
data.resetIdAndVersion();
|
data.resetIdAndVersion();
|
||||||
|
|
||||||
if (zipFile.getZipError() != UNZ_OK) {
|
|
||||||
qCritical().nospace() << "Failed to unzip " << ENTITIES_BACKUP_FILENAME << ": " << zipFile.getZipError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile entitiesFile { _entitiesReplacementFilePath };
|
QFile entitiesFile { _entitiesReplacementFilePath };
|
||||||
|
|
||||||
if (entitiesFile.open(QIODevice::WriteOnly)) {
|
if (entitiesFile.open(QIODevice::WriteOnly)) {
|
||||||
entitiesFile.write(data.toGzippedByteArray());
|
entitiesFile.write(data.toGzippedByteArray());
|
||||||
}
|
}
|
||||||
|
return { true, QString() };
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
void createBackup(const QString& backupName, QuaZip& zip) override;
|
void createBackup(const QString& backupName, QuaZip& zip) override;
|
||||||
|
|
||||||
// Recover from a full backup
|
// Recover from a full backup
|
||||||
void recoverBackup(const QString& backupName, QuaZip& zip) override;
|
std::pair<bool, QString> recoverBackup(const QString& backupName, QuaZip& zip, const QString& sourceFilename) override;
|
||||||
|
|
||||||
// Delete a skeleton backup
|
// Delete a skeleton backup
|
||||||
void deleteBackup(const QString& backupName) override {}
|
void deleteBackup(const QString& backupName) override {}
|
||||||
|
|
|
@ -39,8 +39,8 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
AudioScriptingInterface.noiseGateOpened.connect(function() { root.gated = false; });
|
AudioScriptingInterface.noiseGateOpened.connect(function() { micBar.gated = false; });
|
||||||
AudioScriptingInterface.noiseGateClosed.connect(function() { root.gated = true; });
|
AudioScriptingInterface.noiseGateClosed.connect(function() { micBar.gated = true; });
|
||||||
HMD.displayModeChanged.connect(function() {
|
HMD.displayModeChanged.connect(function() {
|
||||||
muted = AudioScriptingInterface.muted;
|
muted = AudioScriptingInterface.muted;
|
||||||
pushToTalk = AudioScriptingInterface.pushToTalk;
|
pushToTalk = AudioScriptingInterface.pushToTalk;
|
||||||
|
@ -151,7 +151,7 @@ Rectangle {
|
||||||
readonly property string yellow: "#C0C000";
|
readonly property string yellow: "#C0C000";
|
||||||
readonly property string fill: "#55000000";
|
readonly property string fill: "#55000000";
|
||||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||||
readonly property string icon: (muted || clipping) ? mutedColor : root.gated ? gatedColor : unmutedColor;
|
readonly property string icon: (muted || clipping) ? mutedColor : micBar.gated ? gatedColor : unmutedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -169,7 +169,7 @@ Rectangle {
|
||||||
Image {
|
Image {
|
||||||
id: image;
|
id: image;
|
||||||
source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon :
|
source: (pushToTalk) ? pushToTalkIcon : muted ? mutedIcon :
|
||||||
clipping ? clippingIcon : root.gated ? gatedIcon : unmutedIcon;
|
clipping ? clippingIcon : micBar.gated ? gatedIcon : unmutedIcon;
|
||||||
width: 29;
|
width: 29;
|
||||||
height: 32;
|
height: 32;
|
||||||
anchors {
|
anchors {
|
||||||
|
|
|
@ -786,7 +786,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
lightboxPopup.button2text = "CONFIRM";
|
lightboxPopup.button2text = "CONFIRM";
|
||||||
lightboxPopup.button2method = function() {
|
lightboxPopup.button2method = function() {
|
||||||
Commerce.replaceContentSet(root.itemHref, root.certificateId);
|
Commerce.replaceContentSet(root.itemHref, root.certificateId, root.itemName);
|
||||||
lightboxPopup.visible = false;
|
lightboxPopup.visible = false;
|
||||||
rezzedNotifContainer.visible = true;
|
rezzedNotifContainer.visible = true;
|
||||||
rezzedNotifContainerTimer.start();
|
rezzedNotifContainerTimer.start();
|
||||||
|
|
|
@ -36,7 +36,7 @@ Rectangle {
|
||||||
break;
|
break;
|
||||||
case "content set":
|
case "content set":
|
||||||
urlHandler.handleUrl("hifi://localhost/0,0,0");
|
urlHandler.handleUrl("hifi://localhost/0,0,0");
|
||||||
Commerce.replaceContentSet(toUrl(resource), "");
|
Commerce.replaceContentSet(toUrl(resource), "", "");
|
||||||
break;
|
break;
|
||||||
case "entity":
|
case "entity":
|
||||||
case "wearable":
|
case "wearable":
|
||||||
|
|
|
@ -729,7 +729,7 @@ Item {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Tablet.playSound(TabletEnums.ButtonClick);
|
Tablet.playSound(TabletEnums.ButtonClick);
|
||||||
if (root.itemType === "contentSet") {
|
if (root.itemType === "contentSet") {
|
||||||
sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref, certID: root.certificateId});
|
sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref, certID: root.certificateId, itemName: root.itemName});
|
||||||
} else if (root.itemType === "avatar") {
|
} else if (root.itemType === "avatar") {
|
||||||
sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref});
|
sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref});
|
||||||
} else if (root.itemType === "app") {
|
} else if (root.itemType === "app") {
|
||||||
|
|
|
@ -609,7 +609,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
lightboxPopup.button2text = "CONFIRM";
|
lightboxPopup.button2text = "CONFIRM";
|
||||||
lightboxPopup.button2method = function() {
|
lightboxPopup.button2method = function() {
|
||||||
Commerce.replaceContentSet(msg.itemHref, msg.certID);
|
Commerce.replaceContentSet(msg.itemHref, msg.certID, msg.itemName);
|
||||||
lightboxPopup.visible = false;
|
lightboxPopup.visible = false;
|
||||||
};
|
};
|
||||||
lightboxPopup.visible = true;
|
lightboxPopup.visible = true;
|
||||||
|
|
|
@ -1225,8 +1225,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool isStore = property(hifi::properties::OCULUS_STORE).toBool();
|
bool isStore = property(hifi::properties::OCULUS_STORE).toBool();
|
||||||
|
// Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
|
||||||
DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
|
DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore || property(hifi::properties::STEAM).toBool());
|
||||||
|
|
||||||
updateHeartbeat();
|
updateHeartbeat();
|
||||||
|
|
||||||
|
@ -7789,9 +7789,15 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::replaceDomainContent(const QString& url) {
|
static const QString CONTENT_SET_NAME_QUERY_PARAM = "name";
|
||||||
|
|
||||||
|
void Application::replaceDomainContent(const QString& url, const QString& itemName) {
|
||||||
qCDebug(interfaceapp) << "Attempting to replace domain content";
|
qCDebug(interfaceapp) << "Attempting to replace domain content";
|
||||||
QByteArray urlData(url.toUtf8());
|
QUrl msgUrl(url);
|
||||||
|
QUrlQuery urlQuery(msgUrl.query());
|
||||||
|
urlQuery.addQueryItem(CONTENT_SET_NAME_QUERY_PARAM, itemName);
|
||||||
|
msgUrl.setQuery(urlQuery.query(QUrl::QUrl::FullyEncoded));
|
||||||
|
QByteArray urlData(msgUrl.toString(QUrl::QUrl::FullyEncoded).toUtf8());
|
||||||
auto limitedNodeList = DependencyManager::get<NodeList>();
|
auto limitedNodeList = DependencyManager::get<NodeList>();
|
||||||
const auto& domainHandler = limitedNodeList->getDomainHandler();
|
const auto& domainHandler = limitedNodeList->getDomainHandler();
|
||||||
|
|
||||||
|
@ -7825,7 +7831,7 @@ bool Application::askToReplaceDomainContent(const QString& url) {
|
||||||
QString details;
|
QString details;
|
||||||
if (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes) {
|
if (static_cast<QMessageBox::StandardButton>(answer.toInt()) == QMessageBox::Yes) {
|
||||||
// Given confirmation, send request to domain server to replace content
|
// Given confirmation, send request to domain server to replace content
|
||||||
replaceDomainContent(url);
|
replaceDomainContent(url, QString());
|
||||||
details = "SuccessfulRequestToReplaceContent";
|
details = "SuccessfulRequestToReplaceContent";
|
||||||
} else {
|
} else {
|
||||||
details = "UserDeclinedToReplaceContent";
|
details = "UserDeclinedToReplaceContent";
|
||||||
|
|
|
@ -326,7 +326,7 @@ public:
|
||||||
bool isInterstitialMode() const { return _interstitialMode; }
|
bool isInterstitialMode() const { return _interstitialMode; }
|
||||||
bool failedToConnectToEntityServer() const { return _failedToConnectToEntityServer; }
|
bool failedToConnectToEntityServer() const { return _failedToConnectToEntityServer; }
|
||||||
|
|
||||||
void replaceDomainContent(const QString& url);
|
void replaceDomainContent(const QString& url, const QString& itemName);
|
||||||
|
|
||||||
void loadAvatarScripts(const QVector<QString>& urls);
|
void loadAvatarScripts(const QVector<QString>& urls);
|
||||||
void unloadAvatarScripts();
|
void unloadAvatarScripts();
|
||||||
|
|
|
@ -54,6 +54,7 @@ void LODManager::setRenderTimes(float presentTime, float engineRunTime, float ba
|
||||||
}
|
}
|
||||||
|
|
||||||
void LODManager::autoAdjustLOD(float realTimeDelta) {
|
void LODManager::autoAdjustLOD(float realTimeDelta) {
|
||||||
|
std::lock_guard<std::mutex> { _automaticLODLock };
|
||||||
|
|
||||||
// The "render time" is the worse of:
|
// The "render time" is the worse of:
|
||||||
// - engineRunTime: Time spent in the render thread in the engine producing the gpu::Frame N
|
// - engineRunTime: Time spent in the render thread in the engine producing the gpu::Frame N
|
||||||
|
@ -235,6 +236,7 @@ void LODManager::resetLODAdjust() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LODManager::setAutomaticLODAdjust(bool value) {
|
void LODManager::setAutomaticLODAdjust(bool value) {
|
||||||
|
std::lock_guard<std::mutex> { _automaticLODLock };
|
||||||
_automaticLODAdjust = value;
|
_automaticLODAdjust = value;
|
||||||
emit autoLODChanged();
|
emit autoLODChanged();
|
||||||
}
|
}
|
||||||
|
@ -426,7 +428,6 @@ float LODManager::getWorldDetailQuality() const {
|
||||||
return HIGH;
|
return HIGH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LODManager::setLODQualityLevel(float quality) {
|
void LODManager::setLODQualityLevel(float quality) {
|
||||||
_lodQualityLevel = quality;
|
_lodQualityLevel = quality;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef hifi_LODManager_h
|
#ifndef hifi_LODManager_h
|
||||||
#define hifi_LODManager_h
|
#define hifi_LODManager_h
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <OctreeConstants.h>
|
#include <OctreeConstants.h>
|
||||||
|
@ -47,11 +49,6 @@ class AABox;
|
||||||
* @property {number} presentTime <em>Read-only.</em>
|
* @property {number} presentTime <em>Read-only.</em>
|
||||||
* @property {number} engineRunTime <em>Read-only.</em>
|
* @property {number} engineRunTime <em>Read-only.</em>
|
||||||
* @property {number} gpuTime <em>Read-only.</em>
|
* @property {number} gpuTime <em>Read-only.</em>
|
||||||
* @property {number} avgRenderTime <em>Read-only.</em>
|
|
||||||
* @property {number} fps <em>Read-only.</em>
|
|
||||||
* @property {number} lodLevel <em>Read-only.</em>
|
|
||||||
* @property {number} lodDecreaseFPS <em>Read-only.</em>
|
|
||||||
* @property {number} lodIncreaseFPS <em>Read-only.</em>
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class LODManager : public QObject, public Dependency {
|
class LODManager : public QObject, public Dependency {
|
||||||
|
@ -240,6 +237,7 @@ signals:
|
||||||
private:
|
private:
|
||||||
LODManager();
|
LODManager();
|
||||||
|
|
||||||
|
std::mutex _automaticLODLock;
|
||||||
bool _automaticLODAdjust = true;
|
bool _automaticLODAdjust = true;
|
||||||
|
|
||||||
float _presentTime{ 0.0f }; // msec
|
float _presentTime{ 0.0f }; // msec
|
||||||
|
|
|
@ -259,7 +259,7 @@ void QmlCommerce::authorizeAssetTransfer(const QString& couponID,
|
||||||
ledger->authorizeAssetTransfer(key, couponID, certificateID, amount, optionalMessage);
|
ledger->authorizeAssetTransfer(key, couponID, certificateID, amount, optionalMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) {
|
void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID, const QString& itemName) {
|
||||||
if (!certificateID.isEmpty()) {
|
if (!certificateID.isEmpty()) {
|
||||||
auto ledger = DependencyManager::get<Ledger>();
|
auto ledger = DependencyManager::get<Ledger>();
|
||||||
ledger->updateLocation(
|
ledger->updateLocation(
|
||||||
|
@ -267,7 +267,7 @@ void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& cert
|
||||||
DependencyManager::get<AddressManager>()->getPlaceName(),
|
DependencyManager::get<AddressManager>()->getPlaceName(),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
qApp->replaceDomainContent(itemHref);
|
qApp->replaceDomainContent(itemHref, itemName);
|
||||||
QJsonObject messageProperties = {
|
QJsonObject messageProperties = {
|
||||||
{ "status", "SuccessfulRequestToReplaceContent" },
|
{ "status", "SuccessfulRequestToReplaceContent" },
|
||||||
{ "content_set_url", itemHref } };
|
{ "content_set_url", itemHref } };
|
||||||
|
|
|
@ -90,7 +90,7 @@ protected:
|
||||||
Q_INVOKABLE void transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
Q_INVOKABLE void transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||||
Q_INVOKABLE void authorizeAssetTransfer(const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
Q_INVOKABLE void authorizeAssetTransfer(const QString& couponID, const QString& certificateID, const int& amount, const QString& optionalMessage);
|
||||||
|
|
||||||
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID);
|
Q_INVOKABLE void replaceContentSet(const QString& itemHref, const QString& certificateID, const QString& itemName);
|
||||||
|
|
||||||
Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = "");
|
Q_INVOKABLE QString getInstalledApps(const QString& justInstalledAppID = "");
|
||||||
Q_INVOKABLE bool installApp(const QString& appHref, const bool& alsoOpenImmediately = false);
|
Q_INVOKABLE bool installApp(const QString& appHref, const bool& alsoOpenImmediately = false);
|
||||||
|
|
|
@ -136,12 +136,16 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
if (properties.contains(PRESENTATION_MODE_PROPERTY)) {
|
if (properties.contains(PRESENTATION_MODE_PROPERTY)) {
|
||||||
presentationMode = (InteractiveWindowPresentationMode) properties[PRESENTATION_MODE_PROPERTY].toInt();
|
presentationMode = (InteractiveWindowPresentationMode) properties[PRESENTATION_MODE_PROPERTY].toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_interactiveWindowProxy = std::unique_ptr<InteractiveWindowProxy,
|
||||||
|
std::function<void(InteractiveWindowProxy*)>>(new InteractiveWindowProxy, [](InteractiveWindowProxy *p) {
|
||||||
|
p->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
if (!_interactiveWindowProxy) {
|
connect(_interactiveWindowProxy.get(), &InteractiveWindowProxy::webEventReceived,
|
||||||
_interactiveWindowProxy = new InteractiveWindowProxy();
|
this, &InteractiveWindow::emitWebEvent, Qt::QueuedConnection);
|
||||||
QObject::connect(_interactiveWindowProxy, &InteractiveWindowProxy::webEventReceived, this, &InteractiveWindow::emitWebEvent, Qt::QueuedConnection);
|
connect(this, &InteractiveWindow::scriptEventReceived, _interactiveWindowProxy.get(),
|
||||||
QObject::connect(this, &InteractiveWindow::scriptEventReceived, _interactiveWindowProxy, &InteractiveWindowProxy::emitScriptEvent, Qt::QueuedConnection);
|
&InteractiveWindowProxy::emitScriptEvent, Qt::QueuedConnection);
|
||||||
}
|
|
||||||
|
|
||||||
if (properties.contains(DOCKED_PROPERTY) && presentationMode == InteractiveWindowPresentationMode::Native) {
|
if (properties.contains(DOCKED_PROPERTY) && presentationMode == InteractiveWindowPresentationMode::Native) {
|
||||||
QVariantMap nativeWindowInfo = properties[DOCKED_PROPERTY].toMap();
|
QVariantMap nativeWindowInfo = properties[DOCKED_PROPERTY].toMap();
|
||||||
|
@ -161,7 +165,7 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
_dockWidget = std::shared_ptr<DockWidget>(new DockWidget(title, mainWindow), dockWidgetDeleter);
|
_dockWidget = std::shared_ptr<DockWidget>(new DockWidget(title, mainWindow), dockWidgetDeleter);
|
||||||
auto quickView = _dockWidget->getQuickView();
|
auto quickView = _dockWidget->getQuickView();
|
||||||
|
|
||||||
Application::setupQmlSurface(quickView->rootContext() , true);
|
Application::setupQmlSurface(quickView->rootContext(), true);
|
||||||
|
|
||||||
//add any whitelisted callbacks
|
//add any whitelisted callbacks
|
||||||
OffscreenUi::applyWhiteList(sourceUrl, quickView->rootContext());
|
OffscreenUi::applyWhiteList(sourceUrl, quickView->rootContext());
|
||||||
|
@ -201,7 +205,7 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
QObject::connect(quickView.get(), &QQuickView::statusChanged, [&, this] (QQuickView::Status status) {
|
QObject::connect(quickView.get(), &QQuickView::statusChanged, [&, this] (QQuickView::Status status) {
|
||||||
if (status == QQuickView::Ready) {
|
if (status == QQuickView::Ready) {
|
||||||
QQuickItem* rootItem = _dockWidget->getRootItem();
|
QQuickItem* rootItem = _dockWidget->getRootItem();
|
||||||
_dockWidget->getQuickView()->rootContext()->setContextProperty(EVENT_BRIDGE_PROPERTY, _interactiveWindowProxy);
|
_dockWidget->getQuickView()->rootContext()->setContextProperty(EVENT_BRIDGE_PROPERTY, _interactiveWindowProxy.get());
|
||||||
// The qmlToScript method handles the thread-safety of this call. Because the QVariant argument
|
// The qmlToScript method handles the thread-safety of this call. Because the QVariant argument
|
||||||
// passed to the sendToScript signal may wrap an externally managed and thread-unsafe QJSValue,
|
// passed to the sendToScript signal may wrap an externally managed and thread-unsafe QJSValue,
|
||||||
// qmlToScript needs to be called directly, so the QJSValue can be immediately converted to a plain QVariant.
|
// qmlToScript needs to be called directly, so the QJSValue can be immediately converted to a plain QVariant.
|
||||||
|
@ -222,7 +226,7 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
||||||
// Build the event bridge and wrapper on the main thread
|
// Build the event bridge and wrapper on the main thread
|
||||||
offscreenUi->loadInNewContext(CONTENT_WINDOW_QML, [&](QQmlContext* context, QObject* object) {
|
offscreenUi->loadInNewContext(CONTENT_WINDOW_QML, [&](QQmlContext* context, QObject* object) {
|
||||||
_qmlWindowProxy = std::shared_ptr<QmlWindowProxy>(new QmlWindowProxy(object, nullptr), qmlWindowProxyDeleter);
|
_qmlWindowProxy = std::shared_ptr<QmlWindowProxy>(new QmlWindowProxy(object, nullptr), qmlWindowProxyDeleter);
|
||||||
context->setContextProperty(EVENT_BRIDGE_PROPERTY, _interactiveWindowProxy);
|
context->setContextProperty(EVENT_BRIDGE_PROPERTY, _interactiveWindowProxy.get());
|
||||||
if (properties.contains(ADDITIONAL_FLAGS_PROPERTY)) {
|
if (properties.contains(ADDITIONAL_FLAGS_PROPERTY)) {
|
||||||
object->setProperty(ADDITIONAL_FLAGS_PROPERTY, properties[ADDITIONAL_FLAGS_PROPERTY].toUInt());
|
object->setProperty(ADDITIONAL_FLAGS_PROPERTY, properties[ADDITIONAL_FLAGS_PROPERTY].toUInt());
|
||||||
}
|
}
|
||||||
|
@ -313,10 +317,6 @@ void InteractiveWindow::close() {
|
||||||
_qmlWindowProxy->deleteLater();
|
_qmlWindowProxy->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_interactiveWindowProxy) {
|
|
||||||
_interactiveWindowProxy->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_dockWidget) {
|
if (_dockWidget) {
|
||||||
auto window = qApp->getWindow();
|
auto window = qApp->getWindow();
|
||||||
if (QThread::currentThread() != window->thread()) {
|
if (QThread::currentThread() != window->thread()) {
|
||||||
|
|
|
@ -41,7 +41,6 @@ class InteractiveWindowProxy : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
InteractiveWindowProxy(){}
|
InteractiveWindowProxy(){}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void emitScriptEvent(const QVariant& scriptMessage);
|
void emitScriptEvent(const QVariant& scriptMessage);
|
||||||
|
@ -53,7 +52,6 @@ signals:
|
||||||
void webEventReceived(const QVariant& message);
|
void webEventReceived(const QVariant& message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace InteractiveWindowEnums {
|
namespace InteractiveWindowEnums {
|
||||||
Q_NAMESPACE
|
Q_NAMESPACE
|
||||||
|
|
||||||
|
@ -325,7 +323,7 @@ protected slots:
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<QmlWindowProxy> _qmlWindowProxy;
|
std::shared_ptr<QmlWindowProxy> _qmlWindowProxy;
|
||||||
std::shared_ptr<DockWidget> _dockWidget { nullptr };
|
std::shared_ptr<DockWidget> _dockWidget { nullptr };
|
||||||
InteractiveWindowProxy *_interactiveWindowProxy{ nullptr };
|
std::unique_ptr<InteractiveWindowProxy, std::function<void(InteractiveWindowProxy*)>> _interactiveWindowProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef InteractiveWindow* InteractiveWindowPointer;
|
typedef InteractiveWindow* InteractiveWindowPointer;
|
||||||
|
|
|
@ -11,11 +11,52 @@
|
||||||
#include "ResourceImageItem.h"
|
#include "ResourceImageItem.h"
|
||||||
|
|
||||||
#include <gl/Config.h>
|
#include <gl/Config.h>
|
||||||
|
#include <gl/GLHelpers.h>
|
||||||
#include <QOpenGLFramebufferObjectFormat>
|
#include <QOpenGLFramebufferObjectFormat>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
|
||||||
#include <plugins/DisplayPlugin.h>
|
#include <plugins/DisplayPlugin.h>
|
||||||
|
|
||||||
|
|
||||||
|
static const char* VERTEX_SHADER = R"SHADER(
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
const float depth = 0.0;
|
||||||
|
const vec4 UNIT_QUAD[4] = vec4[4](
|
||||||
|
vec4(-1.0, -1.0, depth, 1.0),
|
||||||
|
vec4(1.0, -1.0, depth, 1.0),
|
||||||
|
vec4(-1.0, 1.0, depth, 1.0),
|
||||||
|
vec4(1.0, 1.0, depth, 1.0)
|
||||||
|
);
|
||||||
|
vec4 pos = UNIT_QUAD[gl_VertexID];
|
||||||
|
|
||||||
|
gl_Position = pos;
|
||||||
|
vTexCoord = (pos.xy + 1.0) * 0.5;
|
||||||
|
}
|
||||||
|
)SHADER";
|
||||||
|
|
||||||
|
static const char* FRAGMENT_SHADER = R"SHADER(
|
||||||
|
#version 450 core
|
||||||
|
|
||||||
|
uniform sampler2D sampler;
|
||||||
|
|
||||||
|
in vec2 vTexCoord;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
vec3 color_LinearTosRGB(vec3 lrgb) {
|
||||||
|
return mix(vec3(1.055) * pow(vec3(lrgb), vec3(0.41666)) - vec3(0.055), vec3(lrgb) * vec3(12.92), vec3(lessThan(lrgb, vec3(0.0031308))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
FragColor = vec4(color_LinearTosRGB(texture(sampler, vTexCoord).rgb), 1.0);
|
||||||
|
}
|
||||||
|
)SHADER";
|
||||||
|
|
||||||
|
|
||||||
ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() {
|
ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() {
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update()));
|
connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update()));
|
||||||
|
@ -95,16 +136,29 @@ void ResourceImageItemRenderer::render() {
|
||||||
}
|
}
|
||||||
if (_ready) {
|
if (_ready) {
|
||||||
_fboMutex.lock();
|
_fboMutex.lock();
|
||||||
_copyFbo->bind();
|
|
||||||
QOpenGLFramebufferObject::blitFramebuffer(framebufferObject(), _copyFbo, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
|
||||||
|
|
||||||
// this clears the copyFbo texture
|
|
||||||
// so next frame starts fresh - helps
|
if (!_shader) {
|
||||||
// when aspect ratio changes
|
_shader = new QOpenGLShaderProgram();
|
||||||
_copyFbo->takeTexture();
|
_shader->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, VERTEX_SHADER);
|
||||||
|
_shader->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, FRAGMENT_SHADER);
|
||||||
|
_shader->link();
|
||||||
|
glGenVertexArrays(1, &_vao);
|
||||||
|
}
|
||||||
|
framebufferObject()->bind();
|
||||||
|
_shader->bind();
|
||||||
|
|
||||||
|
auto sourceTextureId = _copyFbo->takeTexture();
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, sourceTextureId);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glBindVertexArray(_vao);
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
glDeleteTextures(1, &sourceTextureId);
|
||||||
|
|
||||||
_copyFbo->bind();
|
_copyFbo->bind();
|
||||||
_copyFbo->release();
|
_copyFbo->release();
|
||||||
|
|
||||||
_fboMutex.unlock();
|
_fboMutex.unlock();
|
||||||
}
|
}
|
||||||
glFlush();
|
glFlush();
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
|
|
||||||
#include <TextureCache.h>
|
#include <TextureCache.h>
|
||||||
|
|
||||||
|
class QOpenGLFramebufferObject;
|
||||||
|
class QOpenGLShaderProgram;
|
||||||
|
|
||||||
class ResourceImageItemRenderer : public QObject, public QQuickFramebufferObject::Renderer {
|
class ResourceImageItemRenderer : public QObject, public QQuickFramebufferObject::Renderer {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -30,14 +33,16 @@ public:
|
||||||
void synchronize(QQuickFramebufferObject* item) override;
|
void synchronize(QQuickFramebufferObject* item) override;
|
||||||
void render() override;
|
void render() override;
|
||||||
private:
|
private:
|
||||||
bool _ready;
|
bool _ready{ false };
|
||||||
QString _url;
|
QString _url;
|
||||||
bool _visible;
|
bool _visible{ false };
|
||||||
|
|
||||||
NetworkTexturePointer _networkTexture;
|
NetworkTexturePointer _networkTexture;
|
||||||
QQuickWindow* _window;
|
QQuickWindow* _window{ nullptr };
|
||||||
QMutex _fboMutex;
|
QMutex _fboMutex;
|
||||||
|
uint32_t _vao{ 0 };
|
||||||
QOpenGLFramebufferObject* _copyFbo { nullptr };
|
QOpenGLFramebufferObject* _copyFbo { nullptr };
|
||||||
|
QOpenGLShaderProgram* _shader{ nullptr };
|
||||||
GLsync _fenceSync { 0 };
|
GLsync _fenceSync { 0 };
|
||||||
QTimer _updateTimer;
|
QTimer _updateTimer;
|
||||||
public slots:
|
public slots:
|
||||||
|
|
|
@ -42,8 +42,7 @@ BOOL CLauncherApp::InitInstance() {
|
||||||
bool uninstalling = false;
|
bool uninstalling = false;
|
||||||
bool restarting = false;
|
bool restarting = false;
|
||||||
bool noUpdate = false;
|
bool noUpdate = false;
|
||||||
bool continueUpdating = false;
|
LauncherManager::ContinueActionOnStart continueAction = LauncherManager::ContinueActionOnStart::ContinueNone;
|
||||||
bool skipSplash = false;
|
|
||||||
if (iNumOfArgs > 1) {
|
if (iNumOfArgs > 1) {
|
||||||
for (int i = 1; i < iNumOfArgs; i++) {
|
for (int i = 1; i < iNumOfArgs; i++) {
|
||||||
CString curArg = CString(pArgs[i]);
|
CString curArg = CString(pArgs[i]);
|
||||||
|
@ -53,10 +52,10 @@ BOOL CLauncherApp::InitInstance() {
|
||||||
restarting = true;
|
restarting = true;
|
||||||
} else if (curArg.Compare(_T("--noUpdate")) == 0) {
|
} else if (curArg.Compare(_T("--noUpdate")) == 0) {
|
||||||
noUpdate = true;
|
noUpdate = true;
|
||||||
} else if (curArg.Compare(_T("--continueUpdating")) == 0) {
|
} else if (curArg.Compare(_T("--continueAction")) == 0) {
|
||||||
continueUpdating = true;
|
if (i + 1 < iNumOfArgs) {
|
||||||
} else if (curArg.Compare(_T("--skipSplash")) == 0) {
|
continueAction = LauncherManager::getContinueActionFromParam(pArgs[i + 1]);
|
||||||
skipSplash = true;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +70,7 @@ BOOL CLauncherApp::InitInstance() {
|
||||||
if (uninstalling) {
|
if (uninstalling) {
|
||||||
_manager.uninstall();
|
_manager.uninstall();
|
||||||
} else {
|
} else {
|
||||||
_manager.init(!noUpdate, continueUpdating, skipSplash);
|
_manager.init(!noUpdate, continueAction);
|
||||||
}
|
}
|
||||||
if (!_manager.hasFailed() && !_manager.installLauncher()) {
|
if (!_manager.hasFailed() && !_manager.installLauncher()) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
|
@ -357,7 +357,7 @@ void CLauncherDlg::drawVoxel(CHwndRenderTarget* pRenderTarget) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLauncherDlg::drawProgress(CHwndRenderTarget* pRenderTarget, float progress, const D2D1::ColorF& color) {
|
void CLauncherDlg::drawProgress(CHwndRenderTarget* pRenderTarget, float progress, const D2D1::ColorF& color) {
|
||||||
auto size = pRenderTarget->GetPixelSize();
|
auto size = pRenderTarget->GetSize();
|
||||||
if (progress == 0.0f) {
|
if (progress == 0.0f) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -693,22 +693,26 @@ void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LauncherManager::ContinueActionOnStart continueAction = theApp._manager.getContinueAction();
|
||||||
if (_showSplash) {
|
if (_showSplash) {
|
||||||
if (_splashStep == 0) {
|
if (_splashStep == 0) {
|
||||||
if (theApp._manager.needsUninstall()) {
|
if (theApp._manager.needsUninstall()) {
|
||||||
theApp._manager.addToLog(_T("Waiting to uninstall"));
|
theApp._manager.addToLog(_T("Waiting to uninstall"));
|
||||||
setDrawDialog(DrawStep::DrawProcessUninstall);
|
setDrawDialog(DrawStep::DrawProcessUninstall);
|
||||||
} else if (theApp._manager.shouldContinueUpdating()) {
|
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueUpdate) {
|
||||||
_splashStep = SPLASH_DURATION;
|
|
||||||
setDrawDialog(DrawStep::DrawProcessUpdate);
|
setDrawDialog(DrawStep::DrawProcessUpdate);
|
||||||
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 0.0f);
|
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 0.0f);
|
||||||
|
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueLogIn) {
|
||||||
|
_splashStep = SPLASH_DURATION;
|
||||||
|
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueFinish) {
|
||||||
|
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 1.0f);
|
||||||
|
setDrawDialog(DrawStep::DrawProcessFinishUpdate);
|
||||||
|
_splashStep = SPLASH_DURATION;
|
||||||
|
_showSplash = false;
|
||||||
} else {
|
} else {
|
||||||
if (theApp._manager.shouldSkipSplashScreen()) {
|
theApp._manager.addToLog(_T("Start splash screen"));
|
||||||
_splashStep = SPLASH_DURATION;
|
setDrawDialog(DrawStep::DrawLogo);
|
||||||
} else {
|
|
||||||
theApp._manager.addToLog(_T("Start splash screen"));
|
|
||||||
setDrawDialog(DrawStep::DrawLogo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (_splashStep > SPLASH_DURATION && !theApp._manager.needsToWait()) {
|
} else if (_splashStep > SPLASH_DURATION && !theApp._manager.needsToWait()) {
|
||||||
_showSplash = false;
|
_showSplash = false;
|
||||||
|
|
|
@ -16,27 +16,55 @@
|
||||||
|
|
||||||
|
|
||||||
LauncherManager::LauncherManager() {
|
LauncherManager::LauncherManager() {
|
||||||
|
int tokenPos = 0;
|
||||||
|
_launcherVersion = CString(LAUNCHER_BUILD_VERSION).Tokenize(_T("-"), tokenPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
LauncherManager::~LauncherManager() {
|
LauncherManager::~LauncherManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LauncherManager::init(BOOL allowUpdate, BOOL continueUpdating, BOOL skipSplashScreen) {
|
void LauncherManager::init(BOOL allowUpdate, ContinueActionOnStart continueAction) {
|
||||||
initLog();
|
initLog();
|
||||||
int tokenPos = 0;
|
|
||||||
_updateLauncherAllowed = allowUpdate;
|
_updateLauncherAllowed = allowUpdate;
|
||||||
_continueUpdating = continueUpdating;
|
_continueAction = continueAction;
|
||||||
_skipSplashScreen = skipSplashScreen;
|
CString msg;
|
||||||
_shouldWait = !skipSplashScreen;
|
msg.Format(_T("Start Screen: %s"), getContinueActionParam(continueAction));
|
||||||
if (_continueUpdating) {
|
addToLog(msg);
|
||||||
|
_shouldWait = _continueAction == ContinueActionOnStart::ContinueNone;
|
||||||
|
if (_continueAction == ContinueActionOnStart::ContinueUpdate) {
|
||||||
_progressOffset = CONTINUE_UPDATING_GLOBAL_OFFSET;
|
_progressOffset = CONTINUE_UPDATING_GLOBAL_OFFSET;
|
||||||
}
|
}
|
||||||
_launcherVersion = CString(LAUNCHER_BUILD_VERSION).Tokenize(_T("-"), tokenPos);
|
|
||||||
addToLog(_T("Launcher is running version: " + _launcherVersion));
|
addToLog(_T("Launcher is running version: " + _launcherVersion));
|
||||||
addToLog(_T("Getting most recent builds"));
|
addToLog(_T("Getting most recent builds"));
|
||||||
getMostRecentBuilds(_latestLauncherURL, _latestLauncherVersion, _latestApplicationURL, _latestVersion);
|
getMostRecentBuilds(_latestLauncherURL, _latestLauncherVersion, _latestApplicationURL, _latestVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CString LauncherManager::getContinueActionParam(LauncherManager::ContinueActionOnStart continueAction) {
|
||||||
|
switch (continueAction) {
|
||||||
|
case LauncherManager::ContinueActionOnStart::ContinueNone:
|
||||||
|
return _T("");
|
||||||
|
case LauncherManager::ContinueActionOnStart::ContinueLogIn:
|
||||||
|
return _T("LogIn");
|
||||||
|
case LauncherManager::ContinueActionOnStart::ContinueUpdate:
|
||||||
|
return _T("Update");
|
||||||
|
case LauncherManager::ContinueActionOnStart::ContinueFinish:
|
||||||
|
return _T("Finish");
|
||||||
|
default:
|
||||||
|
return _T("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LauncherManager::ContinueActionOnStart LauncherManager::getContinueActionFromParam(const CString& param) {
|
||||||
|
if (param.Compare(_T("LogIn")) == 0) {
|
||||||
|
return ContinueActionOnStart::ContinueLogIn;
|
||||||
|
} else if (param.Compare(_T("Update")) == 0) {
|
||||||
|
return ContinueActionOnStart::ContinueUpdate;
|
||||||
|
} else if (param.Compare(_T("Finish")) == 0) {
|
||||||
|
return ContinueActionOnStart::ContinueFinish;
|
||||||
|
} else {
|
||||||
|
return ContinueActionOnStart::ContinueNone;
|
||||||
|
}
|
||||||
|
}
|
||||||
BOOL LauncherManager::initLog() {
|
BOOL LauncherManager::initLog() {
|
||||||
CString logPath;
|
CString logPath;
|
||||||
auto result = getAndCreatePaths(PathType::Launcher_Directory, logPath);
|
auto result = getAndCreatePaths(PathType::Launcher_Directory, logPath);
|
||||||
|
@ -432,7 +460,8 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
||||||
addToLog(updatingMsg);
|
addToLog(updatingMsg);
|
||||||
_shouldUpdateLauncher = TRUE;
|
_shouldUpdateLauncher = TRUE;
|
||||||
_shouldDownloadLauncher = TRUE;
|
_shouldDownloadLauncher = TRUE;
|
||||||
_willContinueUpdating = isInstalled && newInterfaceVersion;
|
_keepLoggingIn = !isInstalled;
|
||||||
|
_keepUpdating = isInstalled && newInterfaceVersion;
|
||||||
} else {
|
} else {
|
||||||
if (_updateLauncherAllowed) {
|
if (_updateLauncherAllowed) {
|
||||||
addToLog(_T("Already running most recent build. Launching interface.exe"));
|
addToLog(_T("Already running most recent build. Launching interface.exe"));
|
||||||
|
@ -612,11 +641,15 @@ void LauncherManager::onFileDownloaded(ProcessType type) {
|
||||||
|
|
||||||
void LauncherManager::restartNewLauncher() {
|
void LauncherManager::restartNewLauncher() {
|
||||||
closeLog();
|
closeLog();
|
||||||
if (_willContinueUpdating) {
|
ContinueActionOnStart continueAction = ContinueActionOnStart::ContinueFinish;
|
||||||
LauncherUtils::launchApplication(_tempLauncherPath, _T(" --restart --noUpdate --continueUpdating"));
|
if (_keepUpdating) {
|
||||||
} else {
|
continueAction = ContinueActionOnStart::ContinueUpdate;
|
||||||
LauncherUtils::launchApplication(_tempLauncherPath, _T(" --restart --noUpdate --skipSplash"));
|
} else if (_keepLoggingIn) {
|
||||||
|
continueAction = ContinueActionOnStart::ContinueLogIn;
|
||||||
}
|
}
|
||||||
|
CStringW params;
|
||||||
|
params.Format(_T(" --restart --noUpdate --continueAction %s"), getContinueActionParam(continueAction));
|
||||||
|
LauncherUtils::launchApplication(_tempLauncherPath, params.GetBuffer());
|
||||||
Sleep(500);
|
Sleep(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "LauncherUtils.h"
|
#include "LauncherUtils.h"
|
||||||
|
#include "LauncherDlg.h"
|
||||||
|
|
||||||
const CString DIRECTORY_NAME_APP = _T("HQ");
|
const CString DIRECTORY_NAME_APP = _T("HQ");
|
||||||
const CString DIRECTORY_NAME_DOWNLOADS = _T("downloads");
|
const CString DIRECTORY_NAME_DOWNLOADS = _T("downloads");
|
||||||
|
@ -27,8 +28,7 @@ const float DOWNLOAD_APPLICATION_UPDATE_WEIGHT = 0.75f;
|
||||||
const float EXTRACT_APPLICATION_UPDATE_WEIGHT = 0.25f;
|
const float EXTRACT_APPLICATION_UPDATE_WEIGHT = 0.25f;
|
||||||
const float CONTINUE_UPDATING_GLOBAL_OFFSET = 0.2f;
|
const float CONTINUE_UPDATING_GLOBAL_OFFSET = 0.2f;
|
||||||
|
|
||||||
class LauncherManager
|
class LauncherManager {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
enum PathType {
|
enum PathType {
|
||||||
Running_Path = 0,
|
Running_Path = 0,
|
||||||
|
@ -57,22 +57,33 @@ public:
|
||||||
UnzipApplication,
|
UnzipApplication,
|
||||||
Uninstall
|
Uninstall
|
||||||
};
|
};
|
||||||
|
enum ContinueActionOnStart {
|
||||||
|
ContinueNone = 0,
|
||||||
|
ContinueLogIn,
|
||||||
|
ContinueUpdate,
|
||||||
|
ContinueFinish
|
||||||
|
};
|
||||||
|
|
||||||
LauncherManager();
|
LauncherManager();
|
||||||
~LauncherManager();
|
~LauncherManager();
|
||||||
void init(BOOL allowUpdate, BOOL continueUpdating, BOOL skipSplashScreen);
|
void init(BOOL allowUpdate, ContinueActionOnStart continueAction);
|
||||||
|
static CString getContinueActionParam(ContinueActionOnStart continueAction);
|
||||||
|
static ContinueActionOnStart getContinueActionFromParam(const CString& param);
|
||||||
BOOL initLog();
|
BOOL initLog();
|
||||||
BOOL addToLog(const CString& line);
|
BOOL addToLog(const CString& line);
|
||||||
void closeLog();
|
void closeLog();
|
||||||
void saveErrorLog();
|
void saveErrorLog();
|
||||||
BOOL getAndCreatePaths(PathType type, CString& outPath);
|
BOOL getAndCreatePaths(PathType type, CString& outPath);
|
||||||
BOOL getInstalledVersion(const CString& path, CString& version);
|
BOOL getInstalledVersion(const CString& path, CString& version);
|
||||||
BOOL isApplicationInstalled(CString& version, CString& domain,
|
BOOL isApplicationInstalled(CString& version, CString& domain,
|
||||||
CString& content, bool& loggedIn);
|
CString& content, bool& loggedIn);
|
||||||
LauncherUtils::ResponseError getAccessTokenForCredentials(const CString& username, const CString& password);
|
LauncherUtils::ResponseError getAccessTokenForCredentials(const CString& username, const CString& password);
|
||||||
void getMostRecentBuilds(CString& launcherUrlOut, CString& launcherVersionOut,
|
void getMostRecentBuilds(CString& launcherUrlOut,
|
||||||
CString& interfaceUrlOut, CString& interfaceVersionOut);
|
CString& launcherVersionOut,
|
||||||
|
CString& interfaceUrlOut,
|
||||||
|
CString& interfaceVersionOut);
|
||||||
LauncherUtils::ResponseError readOrganizationJSON(const CString& hash);
|
LauncherUtils::ResponseError readOrganizationJSON(const CString& hash);
|
||||||
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
|
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
|
||||||
CString& content, bool& loggedIn);
|
CString& content, bool& loggedIn);
|
||||||
BOOL createConfigJSON();
|
BOOL createConfigJSON();
|
||||||
BOOL createApplicationRegistryKeys(int size);
|
BOOL createApplicationRegistryKeys(int size);
|
||||||
|
@ -90,7 +101,6 @@ public:
|
||||||
const CString& getVersion() const { return _version; }
|
const CString& getVersion() const { return _version; }
|
||||||
BOOL shouldShutDown() const { return _shouldShutdown; }
|
BOOL shouldShutDown() const { return _shouldShutdown; }
|
||||||
BOOL shouldLaunch() const { return _shouldLaunch; }
|
BOOL shouldLaunch() const { return _shouldLaunch; }
|
||||||
BOOL shouldSkipSplashScreen() const { return _skipSplashScreen; }
|
|
||||||
BOOL needsUpdate() const { return _shouldUpdate; }
|
BOOL needsUpdate() const { return _shouldUpdate; }
|
||||||
BOOL needsSelfUpdate() const { return _shouldUpdateLauncher; }
|
BOOL needsSelfUpdate() const { return _shouldUpdateLauncher; }
|
||||||
BOOL needsSelfDownload() const { return _shouldDownloadLauncher; }
|
BOOL needsSelfDownload() const { return _shouldDownloadLauncher; }
|
||||||
|
@ -98,14 +108,17 @@ public:
|
||||||
BOOL needsInstall() const { return _shouldInstall; }
|
BOOL needsInstall() const { return _shouldInstall; }
|
||||||
BOOL needsToWait() const { return _shouldWait; }
|
BOOL needsToWait() const { return _shouldWait; }
|
||||||
BOOL needsRestartNewLauncher() const { return _shouldRestartNewLauncher; }
|
BOOL needsRestartNewLauncher() const { return _shouldRestartNewLauncher; }
|
||||||
BOOL shouldContinueUpdating() const { return _continueUpdating; }
|
BOOL willContinueUpdating() const { return _keepUpdating; }
|
||||||
BOOL willContinueUpdating() const { return _willContinueUpdating; }
|
ContinueActionOnStart getContinueAction() { return _continueAction; }
|
||||||
void setDisplayName(const CString& displayName) { _displayName = displayName; }
|
void setDisplayName(const CString& displayName) { _displayName = displayName; }
|
||||||
bool isLoggedIn() const { return _loggedIn; }
|
bool isLoggedIn() const { return _loggedIn; }
|
||||||
bool hasFailed() const { return _hasFailed; }
|
bool hasFailed() const { return _hasFailed; }
|
||||||
void setFailed(bool hasFailed) { _hasFailed = hasFailed; }
|
void setFailed(bool hasFailed) { _hasFailed = hasFailed; }
|
||||||
const CString& getLatestInterfaceURL() const { return _latestApplicationURL; }
|
const CString& getLatestInterfaceURL() const { return _latestApplicationURL; }
|
||||||
void uninstall() { _shouldUninstall = true; _shouldWait = false; };
|
void uninstall() {
|
||||||
|
_shouldUninstall = true;
|
||||||
|
_shouldWait = false;
|
||||||
|
};
|
||||||
|
|
||||||
BOOL downloadFile(ProcessType type, const CString& url, CString& localPath);
|
BOOL downloadFile(ProcessType type, const CString& url, CString& localPath);
|
||||||
BOOL downloadContent();
|
BOOL downloadContent();
|
||||||
|
@ -149,11 +162,10 @@ private:
|
||||||
BOOL _shouldDownloadLauncher { FALSE };
|
BOOL _shouldDownloadLauncher { FALSE };
|
||||||
BOOL _updateLauncherAllowed { TRUE };
|
BOOL _updateLauncherAllowed { TRUE };
|
||||||
BOOL _shouldRestartNewLauncher { FALSE };
|
BOOL _shouldRestartNewLauncher { FALSE };
|
||||||
BOOL _continueUpdating { FALSE };
|
BOOL _keepLoggingIn { FALSE };
|
||||||
BOOL _willContinueUpdating { FALSE };
|
BOOL _keepUpdating { FALSE };
|
||||||
BOOL _skipSplashScreen { FALSE };
|
ContinueActionOnStart _continueAction;
|
||||||
float _progressOffset { 0.0f };
|
float _progressOffset { 0.0f };
|
||||||
float _progress { 0.0f };
|
float _progress { 0.0f };
|
||||||
CStdioFile _logFile;
|
CStdioFile _logFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "HTTPManager.h"
|
#include "HTTPManager.h"
|
||||||
|
|
||||||
const char* HTTPConnection::StatusCode200 = "200 OK";
|
const char* HTTPConnection::StatusCode200 = "200 OK";
|
||||||
|
const char* HTTPConnection::StatusCode204 = "204 No Content";
|
||||||
const char* HTTPConnection::StatusCode301 = "301 Moved Permanently";
|
const char* HTTPConnection::StatusCode301 = "301 Moved Permanently";
|
||||||
const char* HTTPConnection::StatusCode302 = "302 Found";
|
const char* HTTPConnection::StatusCode302 = "302 Found";
|
||||||
const char* HTTPConnection::StatusCode400 = "400 Bad Request";
|
const char* HTTPConnection::StatusCode400 = "400 Bad Request";
|
||||||
|
|
|
@ -45,6 +45,7 @@ class HTTPConnection : public QObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const char* StatusCode200;
|
static const char* StatusCode200;
|
||||||
|
static const char* StatusCode204;
|
||||||
static const char* StatusCode301;
|
static const char* StatusCode301;
|
||||||
static const char* StatusCode302;
|
static const char* StatusCode302;
|
||||||
static const char* StatusCode400;
|
static const char* StatusCode400;
|
||||||
|
|
|
@ -30,6 +30,7 @@ ScriptAudioInjector::ScriptAudioInjector(const AudioInjectorPointer& injector) :
|
||||||
_injector(injector)
|
_injector(injector)
|
||||||
{
|
{
|
||||||
QObject::connect(injector.data(), &AudioInjector::finished, this, &ScriptAudioInjector::finished);
|
QObject::connect(injector.data(), &AudioInjector::finished, this, &ScriptAudioInjector::finished);
|
||||||
|
connect(injector.data(), &QObject::destroyed, this, &QObject::deleteLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptAudioInjector::~ScriptAudioInjector() {
|
ScriptAudioInjector::~ScriptAudioInjector() {
|
||||||
|
|
|
@ -137,7 +137,7 @@ signals:
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioInjectorPointer _injector;
|
QWeakPointer<AudioInjector> _injector;
|
||||||
|
|
||||||
friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in);
|
friend QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in);
|
||||||
};
|
};
|
||||||
|
|
|
@ -531,21 +531,21 @@ function maybeUpdateOutputDeviceMutedOverlay() {
|
||||||
|
|
||||||
|
|
||||||
var oldAutomaticLODAdjust;
|
var oldAutomaticLODAdjust;
|
||||||
var oldLODLevel;
|
var oldLODAngleDeg;
|
||||||
var DEFAULT_AUTO_LOD_ADJUST = false;
|
var SIMPLIFIED_UI_AUTO_LOD_ADJUST = false;
|
||||||
var DEFAULT_LOD_LEVEL = 0.5;
|
var SIMPLIFIED_UI_LOD_ANGLE_DEG = 0.5;
|
||||||
function modifyLODSettings() {
|
function modifyLODSettings() {
|
||||||
oldAutomaticLODAdjust = LODManager.automaticLODAdjust;
|
oldAutomaticLODAdjust = LODManager.automaticLODAdjust;
|
||||||
oldLODLevel = LODManager.lodQualityLevel;
|
oldLODAngleDeg = LODManager.lodAngleDeg;
|
||||||
|
|
||||||
LODManager.automaticLODAdjust = DEFAULT_AUTO_LOD_ADJUST;
|
LODManager.automaticLODAdjust = SIMPLIFIED_UI_AUTO_LOD_ADJUST;
|
||||||
LODManager.lodQualityLevel = DEFAULT_LOD_LEVEL;
|
LODManager.lodAngleDeg = SIMPLIFIED_UI_LOD_ANGLE_DEG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function restoreLODSettings() {
|
function restoreLODSettings() {
|
||||||
LODManager.automaticLODAdjust = oldAutomaticLODAdjust;
|
LODManager.automaticLODAdjust = oldAutomaticLODAdjust;
|
||||||
LODManager.lodQualityLevel = oldLODLevel;
|
LODManager.lodAngleDeg = oldLODAngleDeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue