mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 19:02:55 +02:00
fixing merge conflicts
This commit is contained in:
commit
54a827c67f
14 changed files with 9644 additions and 76 deletions
|
@ -127,7 +127,12 @@ set(src_files
|
||||||
src/LauncherWindow.h
|
src/LauncherWindow.h
|
||||||
src/LauncherWindow.cpp
|
src/LauncherWindow.cpp
|
||||||
src/PathUtils.cpp
|
src/PathUtils.cpp
|
||||||
src/PathUtils.h)
|
src/PathUtils.h
|
||||||
|
src/Unzipper.h
|
||||||
|
src/Unzipper.cpp
|
||||||
|
deps/miniz/miniz.h
|
||||||
|
deps/miniz/miniz.cpp
|
||||||
|
)
|
||||||
|
|
||||||
set(TARGET_NAME ${PROJECT_NAME})
|
set(TARGET_NAME ${PROJECT_NAME})
|
||||||
|
|
||||||
|
@ -176,6 +181,7 @@ elseif (APPLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/deps/
|
||||||
${Qt5Core_INCLUDE_DIRS}
|
${Qt5Core_INCLUDE_DIRS}
|
||||||
${Qt5Quick_INCLUDE_DIRS}
|
${Qt5Quick_INCLUDE_DIRS}
|
||||||
${Qt5Gui_INCLUDE_DIRS}
|
${Qt5Gui_INCLUDE_DIRS}
|
||||||
|
|
7657
launchers/qt/deps/miniz/miniz.cpp
Normal file
7657
launchers/qt/deps/miniz/miniz.cpp
Normal file
File diff suppressed because it is too large
Load diff
1338
launchers/qt/deps/miniz/miniz.h
Normal file
1338
launchers/qt/deps/miniz/miniz.h
Normal file
File diff suppressed because it is too large
Load diff
0
launchers/qt/deps/miniz/stdafx.h
Normal file
0
launchers/qt/deps/miniz/stdafx.h
Normal file
|
@ -62,6 +62,8 @@ Item {
|
||||||
width: 394
|
width: 394
|
||||||
height: 8
|
height: 8
|
||||||
|
|
||||||
|
value: LauncherState.downloadProgress;
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: secondText.bottom
|
top: secondText.bottom
|
||||||
topMargin: 30
|
topMargin: 30
|
||||||
|
@ -88,14 +90,14 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PropertyAnimation {
|
//PropertyAnimation {
|
||||||
target: progressBar;
|
//target: progressBar;
|
||||||
loops: Animation.Infinite
|
//loops: Animation.Infinite
|
||||||
property: "value"
|
//property: "value"
|
||||||
from: 0;
|
//from: 0;
|
||||||
to: 1;
|
//to: 1;
|
||||||
duration: 5000
|
//duration: 5000
|
||||||
running: true
|
//running: true
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ TextField {
|
||||||
horizontalAlignment: TextInput.AlignLeft
|
horizontalAlignment: TextInput.AlignLeft
|
||||||
placeholderText: "PlaceHolder"
|
placeholderText: "PlaceHolder"
|
||||||
property string seperatorColor: "#FFFFFF"
|
property string seperatorColor: "#FFFFFF"
|
||||||
|
selectByMouse: true
|
||||||
background: Item {
|
background: Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.1
|
||||||
import HQLauncher 1.0
|
import HQLauncher 1.0
|
||||||
import "HFControls"
|
import "HFControls"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
width: 515
|
width: 515
|
||||||
|
@ -21,4 +22,14 @@ Item {
|
||||||
loader.source = url;
|
loader.source = url;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
font.pixelSize: 12
|
||||||
|
|
||||||
|
anchors.right: root.right
|
||||||
|
anchors.bottom: root.bottom
|
||||||
|
|
||||||
|
color: "#FFFFFF"
|
||||||
|
text: LauncherState.uiState.toString() + " - " + LauncherState.applicationState
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ Launcher::Launcher(int& argc, char**argv) : QGuiApplication(argc, argv) {
|
||||||
QString resourceBinaryLocation = QGuiApplication::applicationDirPath() + "/resources.rcc";
|
QString resourceBinaryLocation = QGuiApplication::applicationDirPath() + "/resources.rcc";
|
||||||
QResource::registerResource(resourceBinaryLocation);
|
QResource::registerResource(resourceBinaryLocation);
|
||||||
_launcherState = std::make_shared<LauncherState>();
|
_launcherState = std::make_shared<LauncherState>();
|
||||||
_launcherState->setUIState(LauncherState::SPLASH_SCREEN);
|
//_launcherState->setUIState(LauncherState::SPLASH_SCREEN);
|
||||||
_launcherWindow = std::make_unique<LauncherWindow>();
|
_launcherWindow = std::make_unique<LauncherWindow>();
|
||||||
_launcherWindow->rootContext()->setContextProperty("LauncherState", _launcherState.get());
|
_launcherWindow->rootContext()->setContextProperty("LauncherState", _launcherState.get());
|
||||||
_launcherWindow->rootContext()->setContextProperty("PathUtils", new PathUtils());
|
_launcherWindow->rootContext()->setContextProperty("PathUtils", new PathUtils());
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#include "LauncherState.h"
|
#include "LauncherState.h"
|
||||||
|
|
||||||
#include "PathUtils.h"
|
#include "PathUtils.h"
|
||||||
|
#include "Unzipper.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
@ -14,15 +19,121 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
#include <QThreadPool>
|
||||||
|
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
|
||||||
|
bool LatestBuilds::getBuild(QString tag, Build* outBuild) {
|
||||||
|
if (tag.isNull()) {
|
||||||
|
tag = defaultTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& build : builds) {
|
||||||
|
if (build.tag == tag) {
|
||||||
|
*outBuild = build;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static const std::array<QString, LauncherState::UIState::UI_STATE_NUM> QML_FILE_FOR_UI_STATE =
|
static const std::array<QString, LauncherState::UIState::UI_STATE_NUM> QML_FILE_FOR_UI_STATE =
|
||||||
{ { "SplashScreen.qml", "HFBase/CreateAccountBase.qml", "DisplayName.qml",
|
{ { "SplashScreen.qml", "qml/HFBase/CreateAccountBase.qml", "DisplayName.qml",
|
||||||
"Download.qml", "DownloadFinished.qml", "HFBase/Error.qml" } };
|
"Download.qml", "DownloadFinished.qml", "qml/HFBase/Error.qml" } };
|
||||||
|
|
||||||
|
void LauncherState::ASSERT_STATE(LauncherState::ApplicationState state) {
|
||||||
|
if (_applicationState != state) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::ASSERT_STATE(std::vector<LauncherState::ApplicationState> states) {
|
||||||
|
for (auto state : states) {
|
||||||
|
if (_applicationState == state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
}
|
||||||
|
|
||||||
LauncherState::LauncherState() {
|
LauncherState::LauncherState() {
|
||||||
|
_launcherDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
|
// TODO Fix launcher directory
|
||||||
|
qDebug() << "Launcher directory: " << _launcherDirectory.absolutePath();
|
||||||
|
_launcherDirectory.mkpath(_launcherDirectory.absolutePath());
|
||||||
|
requestBuilds();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LauncherState::getCurrentUISource() const {
|
||||||
|
return QML_FILE_FOR_UI_STATE[getUIState()];
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::declareQML() {
|
||||||
|
qmlRegisterType<LauncherState>("HQLauncher", 1, 0, "LauncherStateEnums");
|
||||||
|
}
|
||||||
|
|
||||||
|
LauncherState::UIState LauncherState::getUIState() const {
|
||||||
|
switch (_applicationState) {
|
||||||
|
case ApplicationState::Init:
|
||||||
|
case ApplicationState::RequestingBuilds:
|
||||||
|
return SPLASH_SCREEN;
|
||||||
|
case ApplicationState::WaitingForLogin:
|
||||||
|
case ApplicationState::RequestingLogin:
|
||||||
|
return LOGIN_SCREEN;
|
||||||
|
case ApplicationState::DownloadingClient:
|
||||||
|
case ApplicationState::InstallingClient:
|
||||||
|
case ApplicationState::DownloadingContentCache:
|
||||||
|
case ApplicationState::InstallingContentCache:
|
||||||
|
return DOWNLOAD_SCREEN;
|
||||||
|
case ApplicationState::LaunchingHighFidelity:
|
||||||
|
return DOWNLOAD_FINSISHED;
|
||||||
|
case ApplicationState::UnexpectedError:
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
return ERROR_SCREEN;
|
||||||
|
default:
|
||||||
|
qDebug() << "FATAL: No UI for" << _applicationState;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
return ERROR_SCREEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::setLastLoginError(LastLoginError lastLoginError) {
|
||||||
|
_lastLoginError = lastLoginError;
|
||||||
|
}
|
||||||
|
|
||||||
|
LauncherState::LastLoginError LauncherState::getLastLoginError() const {
|
||||||
|
return _lastLoginError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::requestBuilds() {
|
||||||
|
ASSERT_STATE(ApplicationState::Init);
|
||||||
|
setApplicationState(ApplicationState::RequestingBuilds);
|
||||||
|
|
||||||
// TODO Show splash screen until this request is complete
|
// TODO Show splash screen until this request is complete
|
||||||
auto request = new QNetworkRequest(QUrl("https://thunder.highfidelity.com/builds/api/tags/latest/?format=json"));
|
auto request = new QNetworkRequest(QUrl("https://thunder.highfidelity.com/builds/api/tags/latest/?format=json"));
|
||||||
auto reply = _networkAccessManager.get(*request);
|
auto reply = _networkAccessManager.get(*request);
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [reply, this]() {
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, this, &LauncherState::receivedBuildsReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::receivedBuildsReply() {
|
||||||
|
auto reply = static_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
|
ASSERT_STATE(ApplicationState::RequestingBuilds);
|
||||||
|
|
||||||
if (reply->error()) {
|
if (reply->error()) {
|
||||||
qDebug() << "Error getting builds from thunder: " << reply->errorString();
|
qDebug() << "Error getting builds from thunder: " << reply->errorString();
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,6 +146,7 @@ LauncherState::LauncherState() {
|
||||||
} else {
|
} else {
|
||||||
auto root = doc.object();
|
auto root = doc.object();
|
||||||
if (!root.contains("default_tag")) {
|
if (!root.contains("default_tag")) {
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +154,7 @@ LauncherState::LauncherState() {
|
||||||
|
|
||||||
auto results = root["results"];
|
auto results = root["results"];
|
||||||
if (!results.isArray()) {
|
if (!results.isArray()) {
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,36 +175,14 @@ LauncherState::LauncherState() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//setUIState(LauncherState::LOGIN_SCREEN);
|
setApplicationState(ApplicationState::WaitingForLogin);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LauncherState::getCurrentUISource() const {
|
|
||||||
return QML_FILE_FOR_UI_STATE[getUIState()];
|
|
||||||
}
|
|
||||||
|
|
||||||
void LauncherState::declareQML() {
|
|
||||||
qmlRegisterType<LauncherState>("HQLauncher", 1, 0, "LauncherStateEnums");
|
|
||||||
}
|
|
||||||
|
|
||||||
void LauncherState::setUIState(UIState state) {
|
|
||||||
_uiState = state;
|
|
||||||
emit updateSourceUrl(PathUtils::resourcePath(getCurrentUISource()));
|
|
||||||
}
|
|
||||||
|
|
||||||
LauncherState::UIState LauncherState::getUIState() const {
|
|
||||||
return _uiState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LauncherState::setLastLoginError(LastLoginError lastLoginError) {
|
|
||||||
_lastLoginError = lastLoginError;
|
|
||||||
}
|
|
||||||
|
|
||||||
LauncherState::LastLoginError LauncherState::getLastLoginError() const {
|
|
||||||
return _lastLoginError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LauncherState::login(QString username, QString password) {
|
void LauncherState::login(QString username, QString password) {
|
||||||
|
ASSERT_STATE(ApplicationState::WaitingForLogin);
|
||||||
|
|
||||||
|
setApplicationState(ApplicationState::RequestingLogin);
|
||||||
|
|
||||||
qDebug() << "Got login: " << username << password;
|
qDebug() << "Got login: " << username << password;
|
||||||
|
|
||||||
auto request = new QNetworkRequest(QUrl("https://metaverse.highfidelity.com/oauth/token"));
|
auto request = new QNetworkRequest(QUrl("https://metaverse.highfidelity.com/oauth/token"));
|
||||||
|
@ -104,8 +195,294 @@ void LauncherState::login(QString username, QString password) {
|
||||||
query.addQueryItem("scope", "owner");
|
query.addQueryItem("scope", "owner");
|
||||||
|
|
||||||
auto reply = _networkAccessManager.post(*request, query.toString().toUtf8());
|
auto reply = _networkAccessManager.post(*request, query.toString().toUtf8());
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, this, &LauncherState::receivedLoginReply);
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [reply, this]() {
|
}
|
||||||
qDebug() << "Got response for login: " << reply->readAll();
|
|
||||||
});
|
Q_INVOKABLE void LauncherState::receivedLoginReply() {
|
||||||
|
ASSERT_STATE(ApplicationState::RequestingLogin);
|
||||||
|
|
||||||
|
// TODO Check for errors
|
||||||
|
auto reply = static_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
|
if (reply->error()) {
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = reply->readAll();
|
||||||
|
QJsonParseError parseError;
|
||||||
|
auto doc = QJsonDocument::fromJson(data, &parseError);
|
||||||
|
auto root = doc.object();
|
||||||
|
|
||||||
|
if (!root.contains("access_token")
|
||||||
|
|| !root.contains("token_type")
|
||||||
|
|| !root.contains("expires_in")
|
||||||
|
|| !root.contains("refresh_token")
|
||||||
|
|| !root.contains("scope")
|
||||||
|
|| !root.contains("created_at")) {
|
||||||
|
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loginResponse.accessToken = root["access_token"].toString();
|
||||||
|
_loginResponse.refreshToken = root["refresh_token"].toString();
|
||||||
|
_loginResponse.tokenType = root["token_type"].toString();
|
||||||
|
|
||||||
|
qDebug() << "Got response for login: " << data;
|
||||||
|
_loginTokenResponse = data;
|
||||||
|
|
||||||
|
downloadClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LauncherState::getContentCachePath() const {
|
||||||
|
return _launcherDirectory.filePath("cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LauncherState::shouldDownloadContentCache() const {
|
||||||
|
return !_contentCacheURL.isNull() && !QFile::exists(getContentCachePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::downloadClient() {
|
||||||
|
ASSERT_STATE(ApplicationState::RequestingLogin);
|
||||||
|
|
||||||
|
Build build;
|
||||||
|
if (!_latestBuilds.getBuild(_buildTag, &build)) {
|
||||||
|
qDebug() << "Cannot determine latest build";
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_downloadProgress = 0;
|
||||||
|
setApplicationState(ApplicationState::DownloadingClient);
|
||||||
|
|
||||||
|
// Start client download
|
||||||
|
{
|
||||||
|
qDebug() << "Latest build: " << build.tag << build.buildNumber << build.latestVersion << build.installerZipURL;
|
||||||
|
auto request = new QNetworkRequest(QUrl(build.installerZipURL));
|
||||||
|
auto reply = _networkAccessManager.get(*request);
|
||||||
|
|
||||||
|
_clientZipFile.setFileName(_launcherDirectory.absoluteFilePath("client.zip"));
|
||||||
|
|
||||||
|
qDebug() << "Opening " << _clientZipFile.fileName();
|
||||||
|
if (!_clientZipFile.open(QIODevice::WriteOnly)) {
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, &LauncherState::clientDownloadComplete);
|
||||||
|
connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
|
||||||
|
char buf[4096];
|
||||||
|
while (reply->bytesAvailable() > 0) {
|
||||||
|
qint64 size;
|
||||||
|
size = reply->read(buf, (qint64)sizeof(buf));
|
||||||
|
if (size == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_clientZipFile.write(buf, size);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 total) {
|
||||||
|
_downloadProgress = (float)received / (float)total;
|
||||||
|
emit downloadProgressChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::clientDownloadComplete() {
|
||||||
|
ASSERT_STATE(ApplicationState::DownloadingClient);
|
||||||
|
|
||||||
|
_clientZipFile.close();
|
||||||
|
|
||||||
|
installClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::installClient() {
|
||||||
|
ASSERT_STATE(ApplicationState::DownloadingClient);
|
||||||
|
setApplicationState(ApplicationState::InstallingClient);
|
||||||
|
|
||||||
|
auto installDir = _launcherDirectory.absoluteFilePath("interface_install");
|
||||||
|
_launcherDirectory.mkpath("interface_install");
|
||||||
|
|
||||||
|
_downloadProgress = 0;
|
||||||
|
|
||||||
|
qDebug() << "Unzipping " << _clientZipFile.fileName() << " to " << installDir;
|
||||||
|
|
||||||
|
auto unzipper = new Unzipper(_clientZipFile.fileName(), QDir(installDir));
|
||||||
|
unzipper->setAutoDelete(true);
|
||||||
|
connect(unzipper, &Unzipper::progress, this, [this](float progress) {
|
||||||
|
qDebug() << "Unzipper progress: " << progress;
|
||||||
|
_downloadProgress = progress;
|
||||||
|
emit downloadProgressChanged();
|
||||||
|
});
|
||||||
|
connect(unzipper, &Unzipper::finished, this, [this](bool error, QString errorMessage) {
|
||||||
|
if (error) {
|
||||||
|
qDebug() << "Unzipper finished with error: " << errorMessage;
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Unzipper finished without error";
|
||||||
|
downloadContentCache();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
QThreadPool::globalInstance()->start(unzipper);
|
||||||
|
|
||||||
|
//launchClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::downloadContentCache() {
|
||||||
|
ASSERT_STATE(ApplicationState::InstallingClient);
|
||||||
|
|
||||||
|
// Start content set cache download
|
||||||
|
if (shouldDownloadContentCache()) {
|
||||||
|
setApplicationState(ApplicationState::DownloadingContentCache);
|
||||||
|
|
||||||
|
_downloadProgress = 0;
|
||||||
|
|
||||||
|
auto request = new QNetworkRequest(QUrl(_contentCacheURL));
|
||||||
|
auto reply = _networkAccessManager.get(*request);
|
||||||
|
|
||||||
|
_contentZipFile.setFileName(_launcherDirectory.absoluteFilePath("content_cache.zip"));
|
||||||
|
|
||||||
|
qDebug() << "Opening " << _contentZipFile.fileName();
|
||||||
|
if (!_contentZipFile.open(QIODevice::WriteOnly)) {
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, &LauncherState::contentCacheDownloadComplete);
|
||||||
|
connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
|
||||||
|
char buf[4096];
|
||||||
|
while (reply->bytesAvailable() > 0) {
|
||||||
|
qint64 size;
|
||||||
|
size = reply->read(buf, (qint64)sizeof(buf));
|
||||||
|
if (size == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_contentZipFile.write(buf, size);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 total) {
|
||||||
|
_downloadProgress = (float)received / (float)total;
|
||||||
|
emit downloadProgressChanged();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
launchClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::contentCacheDownloadComplete() {
|
||||||
|
ASSERT_STATE(ApplicationState::DownloadingContentCache);
|
||||||
|
|
||||||
|
_contentZipFile.close();
|
||||||
|
|
||||||
|
installContentCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LauncherState::installContentCache() {
|
||||||
|
ASSERT_STATE(ApplicationState::DownloadingContentCache);
|
||||||
|
setApplicationState(ApplicationState::InstallingContentCache);
|
||||||
|
|
||||||
|
auto installDir = getContentCachePath();
|
||||||
|
|
||||||
|
qDebug() << "Unzipping " << _contentZipFile.fileName() << " to " << installDir;
|
||||||
|
|
||||||
|
_downloadProgress = 0;
|
||||||
|
|
||||||
|
auto unzipper = new Unzipper(_contentZipFile.fileName(), QDir(installDir));
|
||||||
|
unzipper->setAutoDelete(true);
|
||||||
|
connect(unzipper, &Unzipper::progress, this, [this](float progress) {
|
||||||
|
qDebug() << "Unzipper progress (content cache): " << progress;
|
||||||
|
_downloadProgress = progress;
|
||||||
|
emit downloadProgressChanged();
|
||||||
|
});
|
||||||
|
connect(unzipper, &Unzipper::finished, this, [this](bool error, QString errorMessage) {
|
||||||
|
if (error) {
|
||||||
|
qDebug() << "Unzipper finished with error: " << errorMessage;
|
||||||
|
setApplicationState(ApplicationState::UnexpectedError);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Unzipper finished without error";
|
||||||
|
launchClient();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
QThreadPool::globalInstance()->start(unzipper);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::launchClient() {
|
||||||
|
ASSERT_STATE({ ApplicationState::InstallingClient, ApplicationState::InstallingContentCache });
|
||||||
|
|
||||||
|
setApplicationState(ApplicationState::LaunchingHighFidelity);
|
||||||
|
|
||||||
|
QDir installDirectory = _launcherDirectory.filePath("interface_install");
|
||||||
|
auto clientPath = installDirectory.absoluteFilePath("interface.exe");
|
||||||
|
|
||||||
|
QString homePath = "hifi://hq";
|
||||||
|
QString defaultScriptsPath = installDirectory.filePath("scripts/simplifiedUIBootstrapper");
|
||||||
|
QString displayName = "fixMe";
|
||||||
|
QString contentCachePath = _launcherDirectory.filePath("cache");
|
||||||
|
|
||||||
|
// TODO Fix parameters
|
||||||
|
QString params = "--url " + homePath
|
||||||
|
+ " --setBookmark hqhome=\"" + homePath + "\""
|
||||||
|
+ " --defaultScriptsOverride " + QDir::toNativeSeparators(defaultScriptsPath)
|
||||||
|
+ " --displayName " + displayName
|
||||||
|
+ " --cache " + contentCachePath;
|
||||||
|
|
||||||
|
if (!_loginTokenResponse.isEmpty()) {
|
||||||
|
params += " --tokens \"" + _loginTokenResponse.replace("\"", "\\\"") + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
STARTUPINFO si;
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
|
||||||
|
// set the size of the structures
|
||||||
|
ZeroMemory(&si, sizeof(si));
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
ZeroMemory(&pi, sizeof(pi));
|
||||||
|
|
||||||
|
// start the program up
|
||||||
|
BOOL success = CreateProcess(
|
||||||
|
clientPath.toUtf8().data(),
|
||||||
|
params.toUtf8().data(),
|
||||||
|
nullptr, // Process handle not inheritable
|
||||||
|
nullptr, // Thread handle not inheritable
|
||||||
|
FALSE, // Set handle inheritance to FALSE
|
||||||
|
CREATE_NEW_CONSOLE, // Opens file in a separate console
|
||||||
|
nullptr, // Use parent's environment block
|
||||||
|
nullptr, // Use parent's starting directory
|
||||||
|
&si, // Pointer to STARTUPINFO structure
|
||||||
|
&pi // Pointer to PROCESS_INFORMATION structure
|
||||||
|
);
|
||||||
|
// Close process and thread handles.
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
exit(0);
|
||||||
|
#elif defined(Q_OS_MACOS)
|
||||||
|
// TODO Implement launching of client
|
||||||
|
#else
|
||||||
|
#error UNSUPPORTED PLATFORM
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherState::setApplicationState(ApplicationState state) {
|
||||||
|
qDebug() << "Changing application state: " << _applicationState << " -> " << state;
|
||||||
|
|
||||||
|
if (state == ApplicationState::UnexpectedError) {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
_applicationState = state;
|
||||||
|
|
||||||
|
emit uiStateChanged();
|
||||||
|
emit updateSourceUrl(PathUtils::resourcePath(getCurrentUISource()));
|
||||||
|
|
||||||
|
emit applicationStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
LauncherState::ApplicationState LauncherState::getApplicationState() const {
|
||||||
|
return _applicationState;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
struct Build {
|
struct Build {
|
||||||
QString tag;
|
QString tag;
|
||||||
|
@ -11,13 +15,25 @@ struct Build {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LatestBuilds {
|
struct LatestBuilds {
|
||||||
|
bool getBuild(QString tag, Build* outBuild);
|
||||||
|
|
||||||
QString defaultTag;
|
QString defaultTag;
|
||||||
std::vector<Build> builds;
|
std::vector<Build> builds;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LoginResponse {
|
||||||
|
QString accessToken;
|
||||||
|
QString tokenType;
|
||||||
|
QString refreshToken;
|
||||||
|
};
|
||||||
|
|
||||||
class LauncherState : public QObject {
|
class LauncherState : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(UIState uiState READ getUIState NOTIFY uiStateChanged);
|
||||||
|
Q_PROPERTY(ApplicationState applicationState READ getApplicationState NOTIFY applicationStateChanged);
|
||||||
|
Q_PROPERTY(float downloadProgress READ getDownloadProgress NOTIFY downloadProgressChanged);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LauncherState();
|
LauncherState();
|
||||||
~LauncherState() = default;
|
~LauncherState() = default;
|
||||||
|
@ -31,7 +47,25 @@ public:
|
||||||
ERROR_SCREEN,
|
ERROR_SCREEN,
|
||||||
UI_STATE_NUM
|
UI_STATE_NUM
|
||||||
};
|
};
|
||||||
Q_ENUMS(UIState)
|
|
||||||
|
enum class ApplicationState {
|
||||||
|
Init,
|
||||||
|
|
||||||
|
UnexpectedError,
|
||||||
|
|
||||||
|
RequestingBuilds,
|
||||||
|
|
||||||
|
WaitingForLogin,
|
||||||
|
RequestingLogin,
|
||||||
|
|
||||||
|
DownloadingClient,
|
||||||
|
DownloadingContentCache,
|
||||||
|
|
||||||
|
InstallingClient,
|
||||||
|
InstallingContentCache,
|
||||||
|
|
||||||
|
LaunchingHighFidelity
|
||||||
|
};
|
||||||
|
|
||||||
enum LastLoginError {
|
enum LastLoginError {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
|
@ -39,29 +73,74 @@ public:
|
||||||
CREDENTIALS,
|
CREDENTIALS,
|
||||||
LAST_ERROR_NUM
|
LAST_ERROR_NUM
|
||||||
};
|
};
|
||||||
Q_ENUMS(LastLoginError)
|
|
||||||
|
Q_ENUM(UIState);
|
||||||
|
Q_ENUM(ApplicationState)
|
||||||
|
Q_ENUM(LastLoginError)
|
||||||
|
|
||||||
Q_INVOKABLE QString getCurrentUISource() const;
|
Q_INVOKABLE QString getCurrentUISource() const;
|
||||||
|
|
||||||
|
void ASSERT_STATE(LauncherState::ApplicationState state);
|
||||||
|
void ASSERT_STATE(std::vector<LauncherState::ApplicationState> states);
|
||||||
|
|
||||||
static void declareQML();
|
static void declareQML();
|
||||||
|
|
||||||
void setUIState(UIState state);
|
|
||||||
UIState getUIState() const;
|
UIState getUIState() const;
|
||||||
|
|
||||||
void setLastLoginError(LastLoginError lastLoginError);
|
void setLastLoginError(LastLoginError lastLoginError);
|
||||||
LastLoginError getLastLoginError() const;
|
LastLoginError getLastLoginError() const;
|
||||||
|
|
||||||
|
void setApplicationState(ApplicationState state);
|
||||||
|
ApplicationState getApplicationState() const;
|
||||||
|
|
||||||
|
// Request builds
|
||||||
|
void requestBuilds();
|
||||||
|
Q_INVOKABLE void receivedBuildsReply();
|
||||||
|
|
||||||
// LOGIN
|
// Login
|
||||||
Q_INVOKABLE void login(QString username, QString password);
|
Q_INVOKABLE void login(QString username, QString password);
|
||||||
|
Q_INVOKABLE void receivedLoginReply();
|
||||||
|
|
||||||
|
// Client
|
||||||
|
void downloadClient();
|
||||||
|
void installClient();
|
||||||
|
|
||||||
|
// Content Cache
|
||||||
|
void downloadContentCache();
|
||||||
|
void installContentCache();
|
||||||
|
|
||||||
|
// Launching
|
||||||
|
void launchClient();
|
||||||
|
|
||||||
|
Q_INVOKABLE float getDownloadProgress() const { return _downloadProgress; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateSourceUrl(QString sourceUrl);
|
void updateSourceUrl(QString sourceUrl);
|
||||||
|
void uiStateChanged();
|
||||||
|
void applicationStateChanged();
|
||||||
|
void downloadProgressChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void clientDownloadComplete();
|
||||||
|
void contentCacheDownloadComplete();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool shouldDownloadContentCache() const;
|
||||||
|
QString getContentCachePath() const;
|
||||||
|
|
||||||
QNetworkAccessManager _networkAccessManager;
|
QNetworkAccessManager _networkAccessManager;
|
||||||
LatestBuilds _latestBuilds;
|
LatestBuilds _latestBuilds;
|
||||||
|
QDir _launcherDirectory;
|
||||||
|
|
||||||
UIState _uiState { SPLASH_SCREEN };
|
// Application State
|
||||||
|
ApplicationState _applicationState { ApplicationState::Init };
|
||||||
|
LoginResponse _loginResponse;
|
||||||
LastLoginError _lastLoginError { NONE };
|
LastLoginError _lastLoginError { NONE };
|
||||||
|
QString _buildTag { QString::null };
|
||||||
|
QString _contentCacheURL{ "https://orgs.highfidelity.com/content-cache/content_cache_small-only_data8.zip" }; // QString::null }; // If null, there is no content cache to download
|
||||||
|
QString _loginTokenResponse;
|
||||||
|
QFile _clientZipFile;
|
||||||
|
QFile _contentZipFile;
|
||||||
|
|
||||||
|
float _downloadProgress { 0 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
void LauncherWindow::keyPressEvent(QKeyEvent* event) {
|
void LauncherWindow::keyPressEvent(QKeyEvent* event) {
|
||||||
QQuickView::keyPressEvent(event);
|
QQuickView::keyPressEvent(event);
|
||||||
if (!event->isAccepted()) {
|
if (!event->isAccepted()) {
|
||||||
|
if (event->key() == Qt::Key_Escape) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
launchers/qt/src/Unzipper.cpp
Normal file
74
launchers/qt/src/Unzipper.cpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include "Unzipper.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <miniz/miniz.h>
|
||||||
|
|
||||||
|
Unzipper::Unzipper(const QString& zipFilePath, const QDir& outputDirectory) :
|
||||||
|
_zipFilePath(zipFilePath), _outputDirectory(outputDirectory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unzipper::run() {
|
||||||
|
qDebug() << "Reading zip file" << _zipFilePath << ", extracting to" << _outputDirectory.absolutePath();
|
||||||
|
|
||||||
|
mz_zip_archive zip_archive;
|
||||||
|
memset(&zip_archive, 0, sizeof(zip_archive));
|
||||||
|
|
||||||
|
auto status = mz_zip_reader_init_file(&zip_archive, _zipFilePath.toUtf8().data(), 0);
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
auto zip_error = mz_zip_get_last_error(&zip_archive);
|
||||||
|
auto zip_error_msg = mz_zip_get_error_string(zip_error);
|
||||||
|
emit finished(true, "Failed to initialize miniz: " + QString::number(zip_error) + " " + zip_error_msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileCount = (int)mz_zip_reader_get_num_files(&zip_archive);
|
||||||
|
|
||||||
|
qDebug() << "Zip archive has a file count of " << fileCount;
|
||||||
|
|
||||||
|
if (fileCount == 0) {
|
||||||
|
mz_zip_reader_end(&zip_archive);
|
||||||
|
emit finished(false, "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mz_zip_archive_file_stat file_stat;
|
||||||
|
if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat)) {
|
||||||
|
mz_zip_reader_end(&zip_archive);
|
||||||
|
emit finished(true, "Zip archive cannot be stat'd");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t totalSize = 0;
|
||||||
|
uint64_t totalCompressedSize = 0;
|
||||||
|
bool _shouldFail = false;
|
||||||
|
for (int i = 0; i < fileCount; i++) {
|
||||||
|
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue;
|
||||||
|
|
||||||
|
QString filename = file_stat.m_filename;
|
||||||
|
QString fullFilename = _outputDirectory.filePath(filename);
|
||||||
|
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
|
||||||
|
if (!_outputDirectory.mkpath(fullFilename)) {
|
||||||
|
mz_zip_reader_end(&zip_archive);
|
||||||
|
emit finished(true, "Unzipping error while creating folder: " + fullFilename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mz_zip_reader_extract_to_file(&zip_archive, i, fullFilename.toUtf8().data(), 0)) {
|
||||||
|
totalCompressedSize += (uint64_t)file_stat.m_comp_size;
|
||||||
|
totalSize += (uint64_t)file_stat.m_uncomp_size;
|
||||||
|
emit progress((float)totalCompressedSize / (float)zip_archive.m_archive_size);
|
||||||
|
} else {
|
||||||
|
emit finished(true, "Unzipping error unzipping file: " + fullFilename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Done unzipping archive, total size:" << totalSize;
|
||||||
|
|
||||||
|
mz_zip_reader_end(&zip_archive);
|
||||||
|
|
||||||
|
emit finished(false, "");
|
||||||
|
}
|
21
launchers/qt/src/Unzipper.h
Normal file
21
launchers/qt/src/Unzipper.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QRunnable>
|
||||||
|
|
||||||
|
class Unzipper : public QObject, public QRunnable {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Unzipper(const QString& zipFilePath, const QDir& outputDirectory);
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void progress(float progress);
|
||||||
|
void finished(bool error, QString errorMessage);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString _zipFilePath;
|
||||||
|
const QDir _outputDirectory;
|
||||||
|
};
|
|
@ -14,9 +14,8 @@ Q_IMPORT_PLUGIN(QtQuick2Plugin);
|
||||||
Q_IMPORT_PLUGIN(QtQuickControls2Plugin);
|
Q_IMPORT_PLUGIN(QtQuickControls2Plugin);
|
||||||
Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin);
|
Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin);
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[]) {
|
||||||
{
|
QString name { "High Fidelity" };
|
||||||
QString name { "HQLauncher" };
|
|
||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
QCoreApplication::setOrganizationName(name);
|
QCoreApplication::setOrganizationName(name);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue