Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into fix_concurrent_task
This commit is contained in:
commit
40b10ecf30
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v23
|
||||
- uses: DeterminateSystems/update-flake-lock@v24
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
|
@ -67,8 +67,10 @@
|
||||
#include "ui/pages/global/MinecraftPage.h"
|
||||
#include "ui/pages/global/ProxyPage.h"
|
||||
|
||||
#include "ui/setupwizard/AutoJavaWizardPage.h"
|
||||
#include "ui/setupwizard/JavaWizardPage.h"
|
||||
#include "ui/setupwizard/LanguageWizardPage.h"
|
||||
#include "ui/setupwizard/LoginWizardPage.h"
|
||||
#include "ui/setupwizard/PasteWizardPage.h"
|
||||
#include "ui/setupwizard/SetupWizard.h"
|
||||
#include "ui/setupwizard/ThemeWizardPage.h"
|
||||
@ -650,6 +652,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
auto defaultEnableAutoJava = m_settings->get("JavaPath").toString().isEmpty();
|
||||
m_settings->registerSetting("AutomaticJavaSwitch", defaultEnableAutoJava);
|
||||
m_settings->registerSetting("AutomaticJavaDownload", defaultEnableAutoJava);
|
||||
m_settings->registerSetting("UserAskedAboutAutomaticJavaDownload", false);
|
||||
|
||||
// Legacy settings
|
||||
m_settings->registerSetting("OnlineFixes", false);
|
||||
@ -1077,13 +1080,15 @@ bool Application::createSetupWizard()
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !m_settings->get("AutomaticJavaDownload").toBool() &&
|
||||
!m_settings->get("AutomaticJavaSwitch").toBool() && !m_settings->get("UserAskedAboutAutomaticJavaDownload").toBool();
|
||||
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||
bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString());
|
||||
bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString());
|
||||
bool login = !m_accounts->anyAccountIsValid() && capabilities() & Application::SupportsMSA;
|
||||
bool themeInterventionRequired = !validWidgets || !validIcons;
|
||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||
|
||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired || askjava || login;
|
||||
if (wizardRequired) {
|
||||
// set default theme after going into theme wizard
|
||||
if (!validIcons)
|
||||
@ -1100,6 +1105,8 @@ bool Application::createSetupWizard()
|
||||
|
||||
if (javaRequired) {
|
||||
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
|
||||
} else if (askjava) {
|
||||
m_setupWizard->addPage(new AutoJavaWizardPage(m_setupWizard));
|
||||
}
|
||||
|
||||
if (pasteInterventionRequired) {
|
||||
@ -1110,11 +1117,14 @@ bool Application::createSetupWizard()
|
||||
m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard));
|
||||
}
|
||||
|
||||
if (login) {
|
||||
m_setupWizard->addPage(new LoginWizardPage(m_setupWizard));
|
||||
}
|
||||
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
||||
m_setupWizard->show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return wizardRequired || login;
|
||||
}
|
||||
|
||||
bool Application::updaterEnabled()
|
||||
@ -1259,16 +1269,23 @@ Application::~Application()
|
||||
|
||||
void Application::messageReceived(const QByteArray& message)
|
||||
{
|
||||
if (status() != Initialized) {
|
||||
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationMessage received;
|
||||
received.parse(message);
|
||||
|
||||
auto& command = received.command;
|
||||
|
||||
if (status() != Initialized) {
|
||||
bool isLoginAtempt = false;
|
||||
if (command == "import") {
|
||||
QString url = received.args["url"];
|
||||
isLoginAtempt = !url.isEmpty() && normalizeImportUrl(url).scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME;
|
||||
}
|
||||
if (!isLoginAtempt) {
|
||||
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (command == "activate") {
|
||||
showMainWindow();
|
||||
} else if (command == "import") {
|
||||
|
@ -181,7 +181,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
virtual void loadSpecificSettings() = 0;
|
||||
|
||||
/// returns a valid update task
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||
virtual QList<Task::Ptr> createUpdateTask() = 0;
|
||||
|
||||
/// returns a valid launcher (task container)
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||
@ -215,7 +215,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
|
||||
virtual QString typeName() const = 0;
|
||||
|
||||
void updateRuntimeContext();
|
||||
virtual void updateRuntimeContext();
|
||||
RuntimeContext runtimeContext() const { return m_runtimeContext; }
|
||||
|
||||
bool hasVersionBroken() const { return m_hasBrokenVersion; }
|
||||
|
@ -160,8 +160,6 @@ set(LAUNCH_SOURCES
|
||||
launch/steps/PreLaunchCommand.h
|
||||
launch/steps/TextPrint.cpp
|
||||
launch/steps/TextPrint.h
|
||||
launch/steps/Update.cpp
|
||||
launch/steps/Update.h
|
||||
launch/steps/QuitAfterGameStop.cpp
|
||||
launch/steps/QuitAfterGameStop.h
|
||||
launch/steps/PrintServers.cpp
|
||||
@ -172,6 +170,8 @@ set(LAUNCH_SOURCES
|
||||
launch/LaunchTask.h
|
||||
launch/LogModel.cpp
|
||||
launch/LogModel.h
|
||||
launch/TaskStepWrapper.cpp
|
||||
launch/TaskStepWrapper.h
|
||||
)
|
||||
|
||||
# Old update system
|
||||
@ -207,6 +207,11 @@ set(ICONS_SOURCES
|
||||
|
||||
# Support for Minecraft instances and launch
|
||||
set(MINECRAFT_SOURCES
|
||||
|
||||
# Logging
|
||||
minecraft/Logging.h
|
||||
minecraft/Logging.cpp
|
||||
|
||||
# Minecraft support
|
||||
minecraft/auth/AccountData.cpp
|
||||
minecraft/auth/AccountData.h
|
||||
@ -290,8 +295,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/ComponentUpdateTask.h
|
||||
minecraft/MinecraftLoadAndCheck.h
|
||||
minecraft/MinecraftLoadAndCheck.cpp
|
||||
minecraft/MinecraftUpdate.h
|
||||
minecraft/MinecraftUpdate.cpp
|
||||
minecraft/MojangVersionFormat.cpp
|
||||
minecraft/MojangVersionFormat.h
|
||||
minecraft/Rule.cpp
|
||||
@ -663,6 +666,22 @@ ecm_qt_declare_logging_category(CORE_SOURCES
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER instanceProfileC
|
||||
CATEGORY_NAME "launcher.instance.profile"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "Profile actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER instanceProfileResolveC
|
||||
CATEGORY_NAME "launcher.instance.profile.resolve"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "Profile component resolusion actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskLogC
|
||||
CATEGORY_NAME "launcher.task"
|
||||
@ -675,7 +694,7 @@ ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskNetLogC
|
||||
CATEGORY_NAME "launcher.task.net"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "task network action"
|
||||
DESCRIPTION "Task network action"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
@ -683,14 +702,14 @@ ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskDownloadLogC
|
||||
CATEGORY_NAME "launcher.task.net.download"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "task network download actions"
|
||||
DESCRIPTION "Task network download actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER taskUploadLogC
|
||||
CATEGORY_NAME "launcher.task.net.upload"
|
||||
DEFAULT_SEVERITY Debug
|
||||
DESCRIPTION "task network upload actions"
|
||||
DESCRIPTION "Task network upload actions"
|
||||
EXPORT "${Launcher_Name}"
|
||||
)
|
||||
|
||||
@ -823,6 +842,10 @@ SET(LAUNCHER_SOURCES
|
||||
ui/setupwizard/PasteWizardPage.h
|
||||
ui/setupwizard/ThemeWizardPage.cpp
|
||||
ui/setupwizard/ThemeWizardPage.h
|
||||
ui/setupwizard/AutoJavaWizardPage.cpp
|
||||
ui/setupwizard/AutoJavaWizardPage.h
|
||||
ui/setupwizard/LoginWizardPage.cpp
|
||||
ui/setupwizard/LoginWizardPage.h
|
||||
|
||||
# GUI - themes
|
||||
ui/themes/FusionTheme.cpp
|
||||
@ -1135,6 +1158,8 @@ endif()
|
||||
qt_wrap_ui(LAUNCHER_UI
|
||||
ui/MainWindow.ui
|
||||
ui/setupwizard/PasteWizardPage.ui
|
||||
ui/setupwizard/AutoJavaWizardPage.ui
|
||||
ui/setupwizard/LoginWizardPage.ui
|
||||
ui/setupwizard/ThemeWizardPage.ui
|
||||
ui/pages/global/AccountListPage.ui
|
||||
ui/pages/global/JavaPage.ui
|
||||
|
@ -921,6 +921,10 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||
if (destination.isEmpty()) {
|
||||
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
||||
}
|
||||
if (!ensureFilePathExists(destination)) {
|
||||
qWarning() << "Destination path can't be created!";
|
||||
return false;
|
||||
}
|
||||
#if defined(Q_OS_MACOS)
|
||||
// Create the Application
|
||||
QDir applicationDirectory =
|
||||
|
@ -53,7 +53,7 @@ class NullInstance : public BaseInstance {
|
||||
QSet<QString> traits() const override { return {}; };
|
||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; }
|
||||
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||
QList<Task::Ptr> createUpdateTask() override { return {}; }
|
||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
||||
|
@ -20,13 +20,13 @@
|
||||
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include "SysInfo.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
struct RuntimeContext {
|
||||
QString javaArchitecture;
|
||||
QString javaRealArchitecture;
|
||||
QString javaPath;
|
||||
QString system;
|
||||
QString system = SysInfo::currentSystem();
|
||||
|
||||
QString mappedJavaRealArchitecture() const
|
||||
{
|
||||
@ -45,8 +45,6 @@ struct RuntimeContext {
|
||||
{
|
||||
javaArchitecture = instanceSettings->get("JavaArchitecture").toString();
|
||||
javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString();
|
||||
javaPath = instanceSettings->get("JavaPath").toString();
|
||||
system = currentSystem();
|
||||
}
|
||||
|
||||
QString getClassifier() const { return system + "-" + mappedJavaRealArchitecture(); }
|
||||
@ -68,21 +66,4 @@ struct RuntimeContext {
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static QString currentSystem()
|
||||
{
|
||||
#if defined(Q_OS_LINUX)
|
||||
return "linux";
|
||||
#elif defined(Q_OS_MACOS)
|
||||
return "osx";
|
||||
#elif defined(Q_OS_WINDOWS)
|
||||
return "windows";
|
||||
#elif defined(Q_OS_FREEBSD)
|
||||
return "freebsd";
|
||||
#elif defined(Q_OS_OPENBSD)
|
||||
return "openbsd";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include "FileSystem.h"
|
||||
|
||||
namespace {
|
||||
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg" } };
|
||||
static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg", "webp" } };
|
||||
}
|
||||
|
||||
namespace IconUtils {
|
||||
|
@ -16,9 +16,8 @@
|
||||
#include "LaunchStep.h"
|
||||
#include "LaunchTask.h"
|
||||
|
||||
void LaunchStep::bind(LaunchTask* parent)
|
||||
LaunchStep::LaunchStep(LaunchTask* parent) : Task(parent), m_parent(parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
|
||||
connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
|
||||
connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
|
||||
|
@ -24,11 +24,8 @@ class LaunchTask;
|
||||
class LaunchStep : public Task {
|
||||
Q_OBJECT
|
||||
public: /* methods */
|
||||
explicit LaunchStep(LaunchTask* parent) : Task(nullptr), m_parent(parent) { bind(parent); };
|
||||
virtual ~LaunchStep() {};
|
||||
|
||||
private: /* methods */
|
||||
void bind(LaunchTask* parent);
|
||||
explicit LaunchStep(LaunchTask* parent);
|
||||
virtual ~LaunchStep() = default;
|
||||
|
||||
signals:
|
||||
void logLines(QStringList lines, MessageLevel::Enum level);
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
#include "MessageLevel.h"
|
||||
#include "java/JavaChecker.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
void LaunchTask::init()
|
||||
|
@ -41,7 +41,6 @@
|
||||
#include "BaseInstance.h"
|
||||
#include "LaunchStep.h"
|
||||
#include "LogModel.h"
|
||||
#include "LoggedProcess.h"
|
||||
#include "MessageLevel.h"
|
||||
|
||||
class LaunchTask : public Task {
|
||||
@ -55,7 +54,7 @@ class LaunchTask : public Task {
|
||||
|
||||
public: /* methods */
|
||||
static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
|
||||
virtual ~LaunchTask() {};
|
||||
virtual ~LaunchTask() = default;
|
||||
|
||||
void appendStep(shared_qobject_ptr<LaunchStep> step);
|
||||
void prependStep(shared_qobject_ptr<LaunchStep> step);
|
||||
|
65
launcher/launch/TaskStepWrapper.cpp
Normal file
65
launcher/launch/TaskStepWrapper.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TaskStepWrapper.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
void TaskStepWrapper::executeTask()
|
||||
{
|
||||
if (m_state == Task::State::AbortedByUser) {
|
||||
emitFailed(tr("Task aborted."));
|
||||
return;
|
||||
}
|
||||
connect(m_task.get(), &Task::finished, this, &TaskStepWrapper::updateFinished);
|
||||
connect(m_task.get(), &Task::progress, this, &TaskStepWrapper::setProgress);
|
||||
connect(m_task.get(), &Task::stepProgress, this, &TaskStepWrapper::propagateStepProgress);
|
||||
connect(m_task.get(), &Task::status, this, &TaskStepWrapper::setStatus);
|
||||
connect(m_task.get(), &Task::details, this, &TaskStepWrapper::setDetails);
|
||||
emit progressReportingRequest();
|
||||
}
|
||||
|
||||
void TaskStepWrapper::proceed()
|
||||
{
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
void TaskStepWrapper::updateFinished()
|
||||
{
|
||||
if (m_task->wasSuccessful()) {
|
||||
m_task.reset();
|
||||
emitSucceeded();
|
||||
} else {
|
||||
QString reason = tr("Instance update failed because: %1\n\n").arg(m_task->failReason());
|
||||
m_task.reset();
|
||||
emit logLine(reason, MessageLevel::Fatal);
|
||||
emitFailed(reason);
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskStepWrapper::canAbort() const
|
||||
{
|
||||
if (m_task) {
|
||||
return m_task->canAbort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskStepWrapper::abort()
|
||||
{
|
||||
if (m_task && m_task->canAbort()) {
|
||||
return m_task->abort();
|
||||
}
|
||||
return Task::abort();
|
||||
}
|
@ -21,12 +21,11 @@
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <net/Mode.h>
|
||||
|
||||
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
|
||||
class Update : public LaunchStep {
|
||||
class TaskStepWrapper : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Update(LaunchTask* parent, Net::Mode mode) : LaunchStep(parent), m_mode(mode) {};
|
||||
virtual ~Update() {};
|
||||
explicit TaskStepWrapper(LaunchTask* parent, Task::Ptr task) : LaunchStep(parent), m_task(task) {};
|
||||
virtual ~TaskStepWrapper() = default;
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
@ -38,7 +37,5 @@ class Update : public LaunchStep {
|
||||
void updateFinished();
|
||||
|
||||
private:
|
||||
Task::Ptr m_updateTask;
|
||||
bool m_aborted = false;
|
||||
Net::Mode m_mode = Net::Mode::Offline;
|
||||
Task::Ptr m_task;
|
||||
};
|
@ -106,6 +106,7 @@ void CheckJava::executeTask()
|
||||
auto vendorString = instance->settings()->get("JavaVendor").toString();
|
||||
printJavaInfo(verString, archString, realArchString, vendorString);
|
||||
}
|
||||
m_parent->instance()->updateRuntimeContext();
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
@ -124,6 +125,7 @@ void CheckJava::checkJavaFinished(const JavaChecker::Result& result)
|
||||
emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error);
|
||||
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
|
||||
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
|
||||
m_parent->instance()->updateRuntimeContext();
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
@ -135,6 +137,7 @@ void CheckJava::checkJavaFinished(const JavaChecker::Result& result)
|
||||
instance->settings()->set("JavaRealArchitecture", result.realPlatform);
|
||||
instance->settings()->set("JavaVendor", result.javaVendor);
|
||||
instance->settings()->set("JavaSignature", m_javaSignature);
|
||||
m_parent->instance()->updateRuntimeContext();
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class CheckJava : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CheckJava(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~CheckJava() {};
|
||||
virtual ~CheckJava() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
@ -24,7 +24,7 @@ class QuitAfterGameStop : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QuitAfterGameStop(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~QuitAfterGameStop() {};
|
||||
virtual ~QuitAfterGameStop() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
@ -1,73 +0,0 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Update.h"
|
||||
#include <launch/LaunchTask.h>
|
||||
|
||||
void Update::executeTask()
|
||||
{
|
||||
if (m_aborted) {
|
||||
emitFailed(tr("Task aborted."));
|
||||
return;
|
||||
}
|
||||
m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
|
||||
if (m_updateTask) {
|
||||
connect(m_updateTask.get(), &Task::finished, this, &Update::updateFinished);
|
||||
connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress);
|
||||
connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propagateStepProgress);
|
||||
connect(m_updateTask.get(), &Task::status, this, &Update::setStatus);
|
||||
connect(m_updateTask.get(), &Task::details, this, &Update::setDetails);
|
||||
emit progressReportingRequest();
|
||||
return;
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void Update::proceed()
|
||||
{
|
||||
m_updateTask->start();
|
||||
}
|
||||
|
||||
void Update::updateFinished()
|
||||
{
|
||||
if (m_updateTask->wasSuccessful()) {
|
||||
m_updateTask.reset();
|
||||
emitSucceeded();
|
||||
} else {
|
||||
QString reason = tr("Instance update failed because: %1\n\n").arg(m_updateTask->failReason());
|
||||
m_updateTask.reset();
|
||||
emit logLine(reason, MessageLevel::Fatal);
|
||||
emitFailed(reason);
|
||||
}
|
||||
}
|
||||
|
||||
bool Update::canAbort() const
|
||||
{
|
||||
if (m_updateTask) {
|
||||
return m_updateTask->canAbort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Update::abort()
|
||||
{
|
||||
m_aborted = true;
|
||||
if (m_updateTask) {
|
||||
if (m_updateTask->canAbort()) {
|
||||
return m_updateTask->abort();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
#include "VersionList.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Index.h"
|
||||
@ -99,7 +100,7 @@ QVariant VersionList::data(const QModelIndex& index, int role) const
|
||||
case VersionPtrRole:
|
||||
return QVariant::fromValue(version);
|
||||
case RecommendedRole:
|
||||
return version->isRecommended();
|
||||
return version->isRecommended() || m_externalRecommendsVersions.contains(version->version());
|
||||
case JavaMajorRole: {
|
||||
auto major = version->version();
|
||||
if (major.startsWith("java")) {
|
||||
@ -192,6 +193,16 @@ void VersionList::parse(const QJsonObject& obj)
|
||||
parseVersionList(obj, this);
|
||||
}
|
||||
|
||||
void VersionList::addExternalRecommends(const QStringList& recommends)
|
||||
{
|
||||
m_externalRecommendsVersions.append(recommends);
|
||||
}
|
||||
|
||||
void VersionList::clearExternalRecommends()
|
||||
{
|
||||
m_externalRecommendsVersions.clear();
|
||||
}
|
||||
|
||||
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
|
||||
static const Meta::Version::Ptr& getBetterVersion(const Meta::Version::Ptr& a, const Meta::Version::Ptr& b)
|
||||
{
|
||||
@ -276,4 +287,35 @@ void VersionList::waitToLoad()
|
||||
task->start();
|
||||
ev.exec();
|
||||
}
|
||||
|
||||
Version::Ptr VersionList::getRecommendedForParent(const QString& uid, const QString& version)
|
||||
{
|
||||
auto foundExplicit = std::find_if(m_versions.begin(), m_versions.end(), [uid, version](Version::Ptr ver) -> bool {
|
||||
auto& reqs = ver->requiredSet();
|
||||
auto parentReq = std::find_if(reqs.begin(), reqs.end(), [uid, version](const Require& req) -> bool {
|
||||
return req.uid == uid && req.equalsVersion == version;
|
||||
});
|
||||
return parentReq != reqs.end() && ver->isRecommended();
|
||||
});
|
||||
if (foundExplicit != m_versions.end()) {
|
||||
return *foundExplicit;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Version::Ptr VersionList::getLatestForParent(const QString& uid, const QString& version)
|
||||
{
|
||||
Version::Ptr latestCompat = nullptr;
|
||||
for (auto ver : m_versions) {
|
||||
auto& reqs = ver->requiredSet();
|
||||
auto parentReq = std::find_if(reqs.begin(), reqs.end(), [uid, version](const Require& req) -> bool {
|
||||
return req.uid == uid && req.equalsVersion == version;
|
||||
});
|
||||
if (parentReq != reqs.end()) {
|
||||
latestCompat = getBetterVersion(latestCompat, ver);
|
||||
}
|
||||
}
|
||||
return latestCompat;
|
||||
}
|
||||
|
||||
} // namespace Meta
|
||||
|
@ -43,6 +43,8 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
void sortVersions() override;
|
||||
|
||||
BaseVersion::Ptr getRecommended() const override;
|
||||
Version::Ptr getRecommendedForParent(const QString& uid, const QString& version);
|
||||
Version::Ptr getLatestForParent(const QString& uid, const QString& version);
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
RoleList providesRoles() const override;
|
||||
@ -70,6 +72,8 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
void merge(const VersionList::Ptr& other);
|
||||
void mergeFromIndex(const VersionList::Ptr& other);
|
||||
void parse(const QJsonObject& obj) override;
|
||||
void addExternalRecommends(const QStringList& recommends);
|
||||
void clearExternalRecommends();
|
||||
|
||||
signals:
|
||||
void nameChanged(const QString& name);
|
||||
@ -79,6 +83,7 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
|
||||
private:
|
||||
QVector<Version::Ptr> m_versions;
|
||||
QStringList m_externalRecommendsVersions;
|
||||
QHash<QString, Version::Ptr> m_lookup;
|
||||
QString m_uid;
|
||||
QString m_name;
|
||||
|
@ -44,10 +44,19 @@
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include "VersionFile.h"
|
||||
#include "meta/Version.h"
|
||||
#include "minecraft/Component.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
const QMap<QString, ModloaderMapEntry> Component::KNOWN_MODLOADERS = {
|
||||
{ "net.neoforged", { ModPlatform::NeoForge, { "net.minecraftforge", "net.fabricmc.fabric-loader", "org.quiltmc.quilt-loader" } } },
|
||||
{ "net.minecraftforge", { ModPlatform::Forge, { "net.neoforged", "net.fabricmc.fabric-loader", "org.quiltmc.quilt-loader" } } },
|
||||
{ "net.fabricmc.fabric-loader", { ModPlatform::Fabric, { "net.minecraftforge", "net.neoforged", "org.quiltmc.quilt-loader" } } },
|
||||
{ "org.quiltmc.quilt-loader", { ModPlatform::Quilt, { "net.minecraftforge", "net.neoforged", "net.fabricmc.fabric-loader" } } },
|
||||
{ "com.mumfrey.liteloader", { ModPlatform::LiteLoader, {} } }
|
||||
};
|
||||
|
||||
Component::Component(PackProfile* parent, const QString& uid)
|
||||
{
|
||||
assert(parent);
|
||||
@ -223,6 +232,22 @@ bool Component::isVersionChangeable()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Component::isKnownModloader()
|
||||
{
|
||||
auto iter = KNOWN_MODLOADERS.find(m_uid);
|
||||
return iter != KNOWN_MODLOADERS.cend();
|
||||
}
|
||||
|
||||
QStringList Component::knownConflictingComponents()
|
||||
{
|
||||
auto iter = KNOWN_MODLOADERS.find(m_uid);
|
||||
if (iter != KNOWN_MODLOADERS.cend()) {
|
||||
return (*iter).knownConflictingComponents;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void Component::setImportant(bool state)
|
||||
{
|
||||
if (m_important != state) {
|
||||
@ -235,7 +260,8 @@ ProblemSeverity Component::getProblemSeverity() const
|
||||
{
|
||||
auto file = getVersionFile();
|
||||
if (file) {
|
||||
return file->getProblemSeverity();
|
||||
auto severity = file->getProblemSeverity();
|
||||
return m_componentProblemSeverity > severity ? m_componentProblemSeverity : severity;
|
||||
}
|
||||
return ProblemSeverity::Error;
|
||||
}
|
||||
@ -244,11 +270,31 @@ const QList<PatchProblem> Component::getProblems() const
|
||||
{
|
||||
auto file = getVersionFile();
|
||||
if (file) {
|
||||
return file->getProblems();
|
||||
auto problems = file->getProblems();
|
||||
problems.append(m_componentProblems);
|
||||
return problems;
|
||||
}
|
||||
return { { ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.") } };
|
||||
}
|
||||
|
||||
void Component::addComponentProblem(ProblemSeverity severity, const QString& description)
|
||||
{
|
||||
if (severity > m_componentProblemSeverity) {
|
||||
m_componentProblemSeverity = severity;
|
||||
}
|
||||
m_componentProblems.append({ severity, description });
|
||||
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
void Component::resetComponentProblems()
|
||||
{
|
||||
m_componentProblems.clear();
|
||||
m_componentProblemSeverity = ProblemSeverity::None;
|
||||
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
void Component::setVersion(const QString& version)
|
||||
{
|
||||
if (version == m_version) {
|
||||
@ -402,3 +448,36 @@ void Component::updateCachedData()
|
||||
emit dataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Component::waitLoadMeta()
|
||||
{
|
||||
if (!m_loaded) {
|
||||
if (!m_metaVersion || !m_metaVersion->isLoaded()) {
|
||||
// wait for the loaded version from meta
|
||||
m_metaVersion = APPLICATION->metadataIndex()->getLoadedVersion(m_uid, m_version);
|
||||
}
|
||||
m_loaded = true;
|
||||
updateCachedData();
|
||||
}
|
||||
}
|
||||
|
||||
void Component::setUpdateAction(UpdateAction action)
|
||||
{
|
||||
m_updateAction = action;
|
||||
}
|
||||
|
||||
UpdateAction Component::getUpdateAction()
|
||||
{
|
||||
return m_updateAction;
|
||||
}
|
||||
|
||||
void Component::clearUpdateAction()
|
||||
{
|
||||
m_updateAction = UpdateAction{ UpdateActionNone{} };
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const Component& comp)
|
||||
{
|
||||
d << "Component(" << comp.m_uid << " : " << comp.m_cachedVersion << ")";
|
||||
return d;
|
||||
}
|
||||
|
@ -4,9 +4,12 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QList>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include "ProblemProvider.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "meta/JsonFormat.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class PackProfile;
|
||||
class LaunchProfile;
|
||||
@ -16,6 +19,36 @@ class VersionList;
|
||||
} // namespace Meta
|
||||
class VersionFile;
|
||||
|
||||
struct UpdateActionChangeVersion {
|
||||
/// version to change to
|
||||
QString targetVersion;
|
||||
};
|
||||
struct UpdateActionLatestRecommendedCompatible {
|
||||
/// Parent uid
|
||||
QString parentUid;
|
||||
QString parentName;
|
||||
/// Parent version
|
||||
QString version;
|
||||
///
|
||||
};
|
||||
struct UpdateActionRemove {};
|
||||
struct UpdateActionImportantChanged {
|
||||
QString oldVersion;
|
||||
};
|
||||
|
||||
using UpdateActionNone = std::monostate;
|
||||
|
||||
using UpdateAction = std::variant<UpdateActionNone,
|
||||
UpdateActionChangeVersion,
|
||||
UpdateActionLatestRecommendedCompatible,
|
||||
UpdateActionRemove,
|
||||
UpdateActionImportantChanged>;
|
||||
|
||||
struct ModloaderMapEntry {
|
||||
ModPlatform::ModLoaderType type;
|
||||
QStringList knownConflictingComponents;
|
||||
};
|
||||
|
||||
class Component : public QObject, public ProblemProvider {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@ -26,6 +59,8 @@ class Component : public QObject, public ProblemProvider {
|
||||
|
||||
virtual ~Component() {}
|
||||
|
||||
static const QMap<QString, ModloaderMapEntry> KNOWN_MODLOADERS;
|
||||
|
||||
void applyTo(LaunchProfile* profile);
|
||||
|
||||
bool isEnabled();
|
||||
@ -38,6 +73,8 @@ class Component : public QObject, public ProblemProvider {
|
||||
bool isRemovable();
|
||||
bool isCustom();
|
||||
bool isVersionChangeable();
|
||||
bool isKnownModloader();
|
||||
QStringList knownConflictingComponents();
|
||||
|
||||
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
|
||||
void setOrder(int order);
|
||||
@ -58,6 +95,8 @@ class Component : public QObject, public ProblemProvider {
|
||||
|
||||
const QList<PatchProblem> getProblems() const override;
|
||||
ProblemSeverity getProblemSeverity() const override;
|
||||
void addComponentProblem(ProblemSeverity severity, const QString& description);
|
||||
void resetComponentProblems();
|
||||
|
||||
void setVersion(const QString& version);
|
||||
bool customize();
|
||||
@ -65,6 +104,12 @@ class Component : public QObject, public ProblemProvider {
|
||||
|
||||
void updateCachedData();
|
||||
|
||||
void waitLoadMeta();
|
||||
|
||||
void setUpdateAction(UpdateAction action);
|
||||
void clearUpdateAction();
|
||||
UpdateAction getUpdateAction();
|
||||
|
||||
signals:
|
||||
void dataChanged();
|
||||
|
||||
@ -102,6 +147,11 @@ class Component : public QObject, public ProblemProvider {
|
||||
std::shared_ptr<Meta::Version> m_metaVersion;
|
||||
std::shared_ptr<VersionFile> m_file;
|
||||
bool m_loaded = false;
|
||||
|
||||
private:
|
||||
QList<PatchProblem> m_componentProblems;
|
||||
ProblemSeverity m_componentProblemSeverity = ProblemSeverity::None;
|
||||
UpdateAction m_updateAction = UpdateAction{ UpdateActionNone{} };
|
||||
};
|
||||
|
||||
using ComponentPtr = shared_qobject_ptr<Component>;
|
||||
|
@ -1,13 +1,16 @@
|
||||
#include "ComponentUpdateTask.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "Component.h"
|
||||
#include "ComponentUpdateTask_p.h"
|
||||
#include "PackProfile.h"
|
||||
#include "PackProfile_p.h"
|
||||
#include "ProblemProvider.h"
|
||||
#include "Version.h"
|
||||
#include "cassert"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/Version.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/ProfileUtils.h"
|
||||
#include "net/Mode.h"
|
||||
@ -15,6 +18,8 @@
|
||||
#include "Application.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "minecraft/Logging.h"
|
||||
|
||||
/*
|
||||
* This is responsible for loading the components of a component list AND resolving dependency issues between them
|
||||
*/
|
||||
@ -36,7 +41,7 @@
|
||||
ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list, QObject* parent) : Task(parent)
|
||||
{
|
||||
d.reset(new ComponentUpdateTaskData);
|
||||
d->m_list = list;
|
||||
d->m_profile = list;
|
||||
d->mode = mode;
|
||||
d->netmode = netmode;
|
||||
}
|
||||
@ -45,7 +50,7 @@ ComponentUpdateTask::~ComponentUpdateTask() {}
|
||||
|
||||
void ComponentUpdateTask::executeTask()
|
||||
{
|
||||
qDebug() << "Loading components";
|
||||
qCDebug(instanceProfileResolveC) << "Loading components";
|
||||
loadComponents();
|
||||
}
|
||||
|
||||
@ -63,7 +68,7 @@ LoadResult composeLoadResult(LoadResult a, LoadResult b)
|
||||
static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net::Mode netmode)
|
||||
{
|
||||
if (component->m_loaded) {
|
||||
qDebug() << component->getName() << "is already loaded";
|
||||
qCDebug(instanceProfileResolveC) << component->getName() << "is already loaded";
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
|
||||
@ -144,10 +149,11 @@ void ComponentUpdateTask::loadComponents()
|
||||
d->remoteLoadSuccessful = true;
|
||||
|
||||
// load all the components OR their lists...
|
||||
for (auto component : d->m_list->d->components) {
|
||||
for (auto component : d->m_profile->d->components) {
|
||||
Task::Ptr loadTask;
|
||||
LoadResult singleResult;
|
||||
RemoteLoadStatus::Type loadType;
|
||||
component->resetComponentProblems();
|
||||
// FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now,
|
||||
// ignore all that...
|
||||
#if 0
|
||||
@ -175,7 +181,8 @@ void ComponentUpdateTask::loadComponents()
|
||||
}
|
||||
result = composeLoadResult(result, singleResult);
|
||||
if (loadTask) {
|
||||
qDebug() << "Remote loading is being run for" << component->getName();
|
||||
qCDebug(instanceProfileResolveC) << d->m_profile->d->m_instance->name() << "|"
|
||||
<< "Remote loading is being run for" << component->getName();
|
||||
connect(loadTask.get(), &Task::succeeded, this, [this, taskIndex]() { remoteLoadSucceeded(taskIndex); });
|
||||
connect(loadTask.get(), &Task::failed, this, [this, taskIndex](const QString& error) { remoteLoadFailed(taskIndex, error); });
|
||||
connect(loadTask.get(), &Task::aborted, this, [this, taskIndex]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
|
||||
@ -192,6 +199,7 @@ void ComponentUpdateTask::loadComponents()
|
||||
switch (result) {
|
||||
case LoadResult::LoadedLocal: {
|
||||
// Everything got loaded. Advance to dependency resolution.
|
||||
performUpdateActions();
|
||||
resolveDependencies(d->mode == Mode::Launch || d->netmode == Net::Mode::Offline);
|
||||
break;
|
||||
}
|
||||
@ -270,8 +278,8 @@ static bool gatherRequirementsFromComponents(const ComponentContainer& input, Re
|
||||
output.erase(componenRequireEx);
|
||||
output.insert(result.outcome);
|
||||
} else {
|
||||
qCritical() << "Conflicting requirements:" << componentRequire.uid << "versions:" << componentRequire.equalsVersion
|
||||
<< ";" << (*found).equalsVersion;
|
||||
qCCritical(instanceProfileResolveC) << "Conflicting requirements:" << componentRequire.uid
|
||||
<< "versions:" << componentRequire.equalsVersion << ";" << (*found).equalsVersion;
|
||||
}
|
||||
succeeded &= result.ok;
|
||||
} else {
|
||||
@ -353,22 +361,22 @@ static bool getTrivialComponentChanges(const ComponentIndex& index, const Requir
|
||||
} while (false);
|
||||
switch (decision) {
|
||||
case Decision::Undetermined:
|
||||
qCritical() << "No decision for" << reqStr;
|
||||
qCCritical(instanceProfileResolveC) << "No decision for" << reqStr;
|
||||
succeeded = false;
|
||||
break;
|
||||
case Decision::Met:
|
||||
qDebug() << reqStr << "Is met.";
|
||||
qCDebug(instanceProfileResolveC) << reqStr << "Is met.";
|
||||
break;
|
||||
case Decision::Missing:
|
||||
qDebug() << reqStr << "Is missing and should be added at" << req.indexOfFirstDependee;
|
||||
qCDebug(instanceProfileResolveC) << reqStr << "Is missing and should be added at" << req.indexOfFirstDependee;
|
||||
toAdd.insert(req);
|
||||
break;
|
||||
case Decision::VersionNotSame:
|
||||
qDebug() << reqStr << "already has different version that can be changed.";
|
||||
qCDebug(instanceProfileResolveC) << reqStr << "already has different version that can be changed.";
|
||||
toChange.insert(req);
|
||||
break;
|
||||
case Decision::LockedVersionNotSame:
|
||||
qDebug() << reqStr << "already has different version that cannot be changed.";
|
||||
qCDebug(instanceProfileResolveC) << reqStr << "already has different version that cannot be changed.";
|
||||
succeeded = false;
|
||||
break;
|
||||
}
|
||||
@ -376,12 +384,48 @@ static bool getTrivialComponentChanges(const ComponentIndex& index, const Requir
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
ComponentContainer ComponentUpdateTask::collectTreeLinked(const QString& uid)
|
||||
{
|
||||
ComponentContainer linked;
|
||||
|
||||
auto& components = d->m_profile->d->components;
|
||||
auto& componentIndex = d->m_profile->d->componentIndex;
|
||||
auto& instance = d->m_profile->d->m_instance;
|
||||
for (auto comp : components) {
|
||||
qCDebug(instanceProfileResolveC) << instance->name() << "|"
|
||||
<< "scanning" << comp->getID() << ":" << comp->getVersion() << "for tree link";
|
||||
auto dep = std::find_if(comp->m_cachedRequires.cbegin(), comp->m_cachedRequires.cend(),
|
||||
[uid](const Meta::Require& req) -> bool { return req.uid == uid; });
|
||||
if (dep != comp->m_cachedRequires.cend()) {
|
||||
qCDebug(instanceProfileResolveC) << instance->name() << "|" << comp->getID() << ":" << comp->getVersion() << "depends on"
|
||||
<< uid;
|
||||
linked.append(comp);
|
||||
}
|
||||
}
|
||||
auto iter = componentIndex.find(uid);
|
||||
if (iter != componentIndex.end()) {
|
||||
ComponentPtr comp = *iter;
|
||||
comp->updateCachedData();
|
||||
qCDebug(instanceProfileResolveC) << instance->name() << "|" << comp->getID() << ":" << comp->getVersion() << "has"
|
||||
<< comp->m_cachedRequires.size() << "dependencies";
|
||||
for (auto dep : comp->m_cachedRequires) {
|
||||
qCDebug(instanceProfileC) << instance->name() << "|" << uid << "depends on" << dep.uid;
|
||||
auto found = componentIndex.find(dep.uid);
|
||||
if (found != componentIndex.end()) {
|
||||
qCDebug(instanceProfileC) << instance->name() << "|" << (*found)->getID() << "is present";
|
||||
linked.append(*found);
|
||||
}
|
||||
}
|
||||
}
|
||||
return linked;
|
||||
}
|
||||
|
||||
// FIXME, TODO: decouple dependency resolution from loading
|
||||
// FIXME: This works directly with the PackProfile internals. It shouldn't! It needs richer data types than PackProfile uses.
|
||||
// FIXME: throw all this away and use a graph
|
||||
void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
{
|
||||
qDebug() << "Resolving dependencies";
|
||||
qCDebug(instanceProfileResolveC) << "Resolving dependencies";
|
||||
/*
|
||||
* this is a naive dependency resolving algorithm. all it does is check for following conditions and react in simple ways:
|
||||
* 1. There are conflicting dependencies on the same uid with different exact version numbers
|
||||
@ -393,8 +437,8 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
*
|
||||
* NOTE: this is a placeholder and should eventually be replaced with something 'serious'
|
||||
*/
|
||||
auto& components = d->m_list->d->components;
|
||||
auto& componentIndex = d->m_list->d->componentIndex;
|
||||
auto& components = d->m_profile->d->components;
|
||||
auto& componentIndex = d->m_profile->d->componentIndex;
|
||||
|
||||
RequireExSet allRequires;
|
||||
QStringList toRemove;
|
||||
@ -402,15 +446,16 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
allRequires.clear();
|
||||
toRemove.clear();
|
||||
if (!gatherRequirementsFromComponents(components, allRequires)) {
|
||||
finalizeComponents();
|
||||
emitFailed(tr("Conflicting requirements detected during dependency checking!"));
|
||||
return;
|
||||
}
|
||||
getTrivialRemovals(components, allRequires, toRemove);
|
||||
if (!toRemove.isEmpty()) {
|
||||
qDebug() << "Removing obsolete components...";
|
||||
qCDebug(instanceProfileResolveC) << "Removing obsolete components...";
|
||||
for (auto& remove : toRemove) {
|
||||
qDebug() << "Removing" << remove;
|
||||
d->m_list->remove(remove);
|
||||
qCDebug(instanceProfileResolveC) << "Removing" << remove;
|
||||
d->m_profile->remove(remove);
|
||||
}
|
||||
}
|
||||
} while (!toRemove.isEmpty());
|
||||
@ -418,10 +463,12 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
RequireExSet toChange;
|
||||
bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange);
|
||||
if (!succeeded) {
|
||||
finalizeComponents();
|
||||
emitFailed(tr("Instance has conflicting dependencies."));
|
||||
return;
|
||||
}
|
||||
if (checkOnly) {
|
||||
finalizeComponents();
|
||||
if (toAdd.size() || toChange.size()) {
|
||||
emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch."));
|
||||
} else {
|
||||
@ -434,14 +481,15 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
if (toAdd.size()) {
|
||||
// add stuff...
|
||||
for (auto& add : toAdd) {
|
||||
auto component = makeShared<Component>(d->m_list, add.uid);
|
||||
auto component = makeShared<Component>(d->m_profile, add.uid);
|
||||
if (!add.equalsVersion.isEmpty()) {
|
||||
// exact version
|
||||
qDebug() << "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee;
|
||||
qCDebug(instanceProfileResolveC)
|
||||
<< "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee;
|
||||
component->m_version = add.equalsVersion;
|
||||
} else {
|
||||
// version needs to be decided
|
||||
qDebug() << "Adding" << add.uid << "at position" << add.indexOfFirstDependee;
|
||||
qCDebug(instanceProfileResolveC) << "Adding" << add.uid << "at position" << add.indexOfFirstDependee;
|
||||
// ############################################################################################################
|
||||
// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
|
||||
if (!add.suggests.isEmpty()) {
|
||||
@ -464,7 +512,7 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
}
|
||||
component->m_dependencyOnly = true;
|
||||
// FIXME: this should not work directly with the component list
|
||||
d->m_list->insertComponent(add.indexOfFirstDependee, component);
|
||||
d->m_profile->insertComponent(add.indexOfFirstDependee, component);
|
||||
componentIndex[add.uid] = component;
|
||||
}
|
||||
recursionNeeded = true;
|
||||
@ -473,7 +521,7 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
// change a version of something that exists
|
||||
for (auto& change : toChange) {
|
||||
// FIXME: this should not work directly with the component list
|
||||
qDebug() << "Setting version of " << change.uid << "to" << change.equalsVersion;
|
||||
qCDebug(instanceProfileResolveC) << "Setting version of " << change.uid << "to" << change.equalsVersion;
|
||||
auto component = componentIndex[change.uid];
|
||||
component->setVersion(change.equalsVersion);
|
||||
}
|
||||
@ -483,14 +531,182 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
if (recursionNeeded) {
|
||||
loadComponents();
|
||||
} else {
|
||||
finalizeComponents();
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
// Variant visitation via lambda
|
||||
template <class... Ts>
|
||||
struct overload : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template <class... Ts>
|
||||
overload(Ts...) -> overload<Ts...>;
|
||||
|
||||
void ComponentUpdateTask::performUpdateActions()
|
||||
{
|
||||
auto& instance = d->m_profile->d->m_instance;
|
||||
bool addedActions;
|
||||
QStringList toRemove;
|
||||
do {
|
||||
addedActions = false;
|
||||
toRemove.clear();
|
||||
auto& components = d->m_profile->d->components;
|
||||
auto& componentIndex = d->m_profile->d->componentIndex;
|
||||
for (auto component : components) {
|
||||
if (!component) {
|
||||
continue;
|
||||
}
|
||||
auto action = component->getUpdateAction();
|
||||
auto visitor =
|
||||
overload{ [](const UpdateActionNone&) {
|
||||
// noop
|
||||
},
|
||||
[&component, &instance](const UpdateActionChangeVersion& cv) {
|
||||
qCDebug(instanceProfileResolveC) << instance->name() << "|"
|
||||
<< "UpdateActionChangeVersion" << component->getID() << ":"
|
||||
<< component->getVersion() << "change to" << cv.targetVersion;
|
||||
component->setVersion(cv.targetVersion);
|
||||
component->waitLoadMeta();
|
||||
},
|
||||
[&component, &instance](const UpdateActionLatestRecommendedCompatible lrc) {
|
||||
qCDebug(instanceProfileResolveC)
|
||||
<< instance->name() << "|"
|
||||
<< "UpdateActionLatestRecommendedCompatible" << component->getID() << ":" << component->getVersion()
|
||||
<< "updating to latest recommend or compatible with" << lrc.parentUid << lrc.version;
|
||||
auto versionList = APPLICATION->metadataIndex()->get(component->getID());
|
||||
if (versionList) {
|
||||
versionList->waitToLoad();
|
||||
auto recommended = versionList->getRecommendedForParent(lrc.parentUid, lrc.version);
|
||||
if (!recommended) {
|
||||
recommended = versionList->getLatestForParent(lrc.parentUid, lrc.version);
|
||||
}
|
||||
if (recommended) {
|
||||
component->setVersion(recommended->version());
|
||||
component->waitLoadMeta();
|
||||
return;
|
||||
} else {
|
||||
component->addComponentProblem(ProblemSeverity::Error,
|
||||
QObject::tr("No compatible version of %1 found for %2 %3")
|
||||
.arg(component->getName(), lrc.parentName, lrc.version));
|
||||
}
|
||||
} else {
|
||||
component->addComponentProblem(
|
||||
ProblemSeverity::Error,
|
||||
QObject::tr("No version list in metadata index for %1").arg(component->getID()));
|
||||
}
|
||||
},
|
||||
[&component, &instance, &toRemove](const UpdateActionRemove&) {
|
||||
qCDebug(instanceProfileResolveC)
|
||||
<< instance->name() << "|"
|
||||
<< "UpdateActionRemove" << component->getID() << ":" << component->getVersion() << "removing";
|
||||
toRemove.append(component->getID());
|
||||
},
|
||||
[this, &component, &instance, &addedActions, &componentIndex](const UpdateActionImportantChanged& ic) {
|
||||
qCDebug(instanceProfileResolveC)
|
||||
<< instance->name() << "|"
|
||||
<< "UpdateImportantChanged" << component->getID() << ":" << component->getVersion() << "was changed from"
|
||||
<< ic.oldVersion << "updating linked components";
|
||||
auto oldVersion = APPLICATION->metadataIndex()->getLoadedVersion(component->getID(), ic.oldVersion);
|
||||
for (auto oldReq : oldVersion->requiredSet()) {
|
||||
auto currentlyRequired = component->m_cachedRequires.find(oldReq);
|
||||
if (currentlyRequired == component->m_cachedRequires.cend()) {
|
||||
auto oldReqComp = componentIndex.find(oldReq.uid);
|
||||
if (oldReqComp != componentIndex.cend()) {
|
||||
(*oldReqComp)->setUpdateAction(UpdateAction{ UpdateActionRemove{} });
|
||||
addedActions = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto linked = collectTreeLinked(component->getID());
|
||||
for (auto comp : linked) {
|
||||
if (comp->isCustom()) {
|
||||
continue;
|
||||
}
|
||||
auto compUid = comp->getID();
|
||||
auto parentReq = std::find_if(component->m_cachedRequires.begin(), component->m_cachedRequires.end(),
|
||||
[compUid](const Meta::Require& req) { return req.uid == compUid; });
|
||||
if (parentReq != component->m_cachedRequires.end()) {
|
||||
auto newVersion = parentReq->equalsVersion.isEmpty() ? parentReq->suggests : parentReq->equalsVersion;
|
||||
if (!newVersion.isEmpty()) {
|
||||
comp->setUpdateAction(UpdateAction{ UpdateActionChangeVersion{ newVersion } });
|
||||
} else {
|
||||
comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatible{
|
||||
component->getID(),
|
||||
component->getName(),
|
||||
component->getVersion(),
|
||||
} });
|
||||
}
|
||||
} else {
|
||||
comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatible{
|
||||
component->getID(),
|
||||
component->getName(),
|
||||
component->getVersion(),
|
||||
} });
|
||||
}
|
||||
addedActions = true;
|
||||
}
|
||||
} };
|
||||
std::visit(visitor, action);
|
||||
component->clearUpdateAction();
|
||||
for (auto uid : toRemove) {
|
||||
d->m_profile->remove(uid);
|
||||
}
|
||||
}
|
||||
} while (addedActions);
|
||||
}
|
||||
|
||||
void ComponentUpdateTask::finalizeComponents()
|
||||
{
|
||||
auto& components = d->m_profile->d->components;
|
||||
auto& componentIndex = d->m_profile->d->componentIndex;
|
||||
for (auto component : components) {
|
||||
for (auto req : component->m_cachedRequires) {
|
||||
auto found = componentIndex.find(req.uid);
|
||||
if (found == componentIndex.cend()) {
|
||||
component->addComponentProblem(
|
||||
ProblemSeverity::Error,
|
||||
QObject::tr("%1 is missing requirement %2 %3")
|
||||
.arg(component->getName(), req.uid, req.equalsVersion.isEmpty() ? req.suggests : req.equalsVersion));
|
||||
} else {
|
||||
auto reqComp = *found;
|
||||
if (!reqComp->getProblems().isEmpty()) {
|
||||
component->addComponentProblem(
|
||||
reqComp->getProblemSeverity(),
|
||||
QObject::tr("%1, a dependency of this component, has reported issues").arg(reqComp->getName()));
|
||||
}
|
||||
if (!req.equalsVersion.isEmpty() && req.equalsVersion != reqComp->getVersion()) {
|
||||
component->addComponentProblem(ProblemSeverity::Error,
|
||||
QObject::tr("%1, a dependency of this component, is not the required version %2")
|
||||
.arg(reqComp->getName(), req.equalsVersion));
|
||||
} else if (!req.suggests.isEmpty() && req.suggests != reqComp->getVersion()) {
|
||||
component->addComponentProblem(ProblemSeverity::Warning,
|
||||
QObject::tr("%1, a dependency of this component, is not the suggested version %2")
|
||||
.arg(reqComp->getName(), req.suggests));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto conflict : component->knownConflictingComponents()) {
|
||||
auto found = componentIndex.find(conflict);
|
||||
if (found != componentIndex.cend()) {
|
||||
auto foundComp = *found;
|
||||
if (foundComp->isCustom()) {
|
||||
continue;
|
||||
}
|
||||
component->addComponentProblem(
|
||||
ProblemSeverity::Warning,
|
||||
QObject::tr("%1 and %2 are known to not work together. It is recommended to remove one of them.")
|
||||
.arg(component->getName(), foundComp->getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
{
|
||||
if (static_cast<size_t>(d->remoteLoadStatusList.size()) < taskIndex) {
|
||||
qWarning() << "Got task index outside of results" << taskIndex;
|
||||
qCWarning(instanceProfileResolveC) << "Got task index outside of results" << taskIndex;
|
||||
return;
|
||||
}
|
||||
auto& taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
@ -498,16 +714,16 @@ void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
disconnect(taskSlot.task.get(), &Task::failed, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr);
|
||||
if (taskSlot.finished) {
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
qCWarning(instanceProfileResolveC) << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
}
|
||||
qDebug() << "Remote task" << taskIndex << "succeeded";
|
||||
qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "succeeded";
|
||||
taskSlot.succeeded = false;
|
||||
taskSlot.finished = true;
|
||||
d->remoteTasksInProgress--;
|
||||
// update the cached data of the component from the downloaded version file.
|
||||
if (taskSlot.type == RemoteLoadStatus::Type::Version) {
|
||||
auto component = d->m_list->getComponent(taskSlot.PackProfileIndex);
|
||||
auto component = d->m_profile->getComponent(taskSlot.PackProfileIndex);
|
||||
component->m_loaded = true;
|
||||
component->updateCachedData();
|
||||
}
|
||||
@ -517,7 +733,7 @@ void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
|
||||
{
|
||||
if (static_cast<size_t>(d->remoteLoadStatusList.size()) < taskIndex) {
|
||||
qWarning() << "Got task index outside of results" << taskIndex;
|
||||
qCWarning(instanceProfileResolveC) << "Got task index outside of results" << taskIndex;
|
||||
return;
|
||||
}
|
||||
auto& taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
@ -525,10 +741,10 @@ void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
|
||||
disconnect(taskSlot.task.get(), &Task::failed, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr);
|
||||
if (taskSlot.finished) {
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
qCWarning(instanceProfileResolveC) << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
}
|
||||
qDebug() << "Remote task" << taskIndex << "failed: " << msg;
|
||||
qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "failed: " << msg;
|
||||
d->remoteLoadSuccessful = false;
|
||||
taskSlot.succeeded = false;
|
||||
taskSlot.finished = true;
|
||||
@ -546,6 +762,7 @@ void ComponentUpdateTask::checkIfAllFinished()
|
||||
if (d->remoteLoadSuccessful) {
|
||||
// nothing bad happened... clear the temp load status and proceed with looking at dependencies
|
||||
d->remoteLoadStatusList.clear();
|
||||
performUpdateActions();
|
||||
resolveDependencies(d->mode == Mode::Launch);
|
||||
} else {
|
||||
// remote load failed... report error and bail
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "minecraft/Component.h"
|
||||
#include "net/Mode.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
@ -21,7 +22,11 @@ class ComponentUpdateTask : public Task {
|
||||
|
||||
private:
|
||||
void loadComponents();
|
||||
/// collects components that are dependent on or dependencies of the component
|
||||
QList<ComponentPtr> collectTreeLinked(const QString& uid);
|
||||
void resolveDependencies(bool checkOnly);
|
||||
void performUpdateActions();
|
||||
void finalizeComponents();
|
||||
|
||||
void remoteLoadSucceeded(size_t index);
|
||||
void remoteLoadFailed(size_t index, const QString& msg);
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include "net/Mode.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "minecraft/ComponentUpdateTask.h"
|
||||
|
||||
class PackProfile;
|
||||
|
||||
struct RemoteLoadStatus {
|
||||
@ -18,7 +20,7 @@ struct RemoteLoadStatus {
|
||||
};
|
||||
|
||||
struct ComponentUpdateTaskData {
|
||||
PackProfile* m_list = nullptr;
|
||||
PackProfile* m_profile = nullptr;
|
||||
QList<RemoteLoadStatus> remoteLoadStatusList;
|
||||
bool remoteLoadSuccessful = true;
|
||||
size_t remoteTasksInProgress = 0;
|
||||
|
@ -164,6 +164,7 @@ void LaunchProfile::applyCompatibleJavaMajors(QList<int>& javaMajor)
|
||||
{
|
||||
m_compatibleJavaMajors.append(javaMajor);
|
||||
}
|
||||
|
||||
void LaunchProfile::applyCompatibleJavaName(QString javaName)
|
||||
{
|
||||
if (!javaName.isEmpty())
|
||||
|
25
launcher/minecraft/Logging.cpp
Normal file
25
launcher/minecraft/Logging.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "minecraft/Logging.h"
|
||||
#include <qloggingcategory.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile")
|
||||
Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve")
|
26
launcher/minecraft/Logging.h
Normal file
26
launcher/minecraft/Logging.h
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(instanceProfileC)
|
||||
Q_DECLARE_LOGGING_CATEGORY(instanceProfileResolveC)
|
@ -43,6 +43,9 @@
|
||||
#include "minecraft/launch/CreateGameFolders.h"
|
||||
#include "minecraft/launch/ExtractNatives.h"
|
||||
#include "minecraft/launch/PrintInstanceInfo.h"
|
||||
#include "minecraft/update/AssetUpdateTask.h"
|
||||
#include "minecraft/update/FMLLibrariesTask.h"
|
||||
#include "minecraft/update/LibrariesTask.h"
|
||||
#include "settings/Setting.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
@ -53,13 +56,13 @@
|
||||
#include "pathmatcher/RegexpMatcher.h"
|
||||
|
||||
#include "launch/LaunchTask.h"
|
||||
#include "launch/TaskStepWrapper.h"
|
||||
#include "launch/steps/CheckJava.h"
|
||||
#include "launch/steps/LookupServerAddress.h"
|
||||
#include "launch/steps/PostLaunchCommand.h"
|
||||
#include "launch/steps/PreLaunchCommand.h"
|
||||
#include "launch/steps/QuitAfterGameStop.h"
|
||||
#include "launch/steps/TextPrint.h"
|
||||
#include "launch/steps/Update.h"
|
||||
|
||||
#include "minecraft/launch/ClaimAccount.h"
|
||||
#include "minecraft/launch/LauncherPartLaunch.h"
|
||||
@ -70,9 +73,6 @@
|
||||
|
||||
#include "java/JavaUtils.h"
|
||||
|
||||
#include "meta/Index.h"
|
||||
#include "meta/VersionList.h"
|
||||
|
||||
#include "icons/IconList.h"
|
||||
|
||||
#include "mod/ModFolderModel.h"
|
||||
@ -84,7 +84,6 @@
|
||||
|
||||
#include "AssetsUtils.h"
|
||||
#include "MinecraftLoadAndCheck.h"
|
||||
#include "MinecraftUpdate.h"
|
||||
#include "PackProfile.h"
|
||||
#include "minecraft/gameoptions/GameOptions.h"
|
||||
#include "minecraft/update/FoldersTask.h"
|
||||
@ -218,6 +217,7 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
void MinecraftInstance::updateRuntimeContext()
|
||||
{
|
||||
m_runtimeContext.updateFromInstanceSettings(m_settings);
|
||||
m_components->invalidateLaunchProfile();
|
||||
}
|
||||
|
||||
QString MinecraftInstance::typeName() const
|
||||
@ -1030,18 +1030,18 @@ QString MinecraftInstance::getStatusbarDescription()
|
||||
return description;
|
||||
}
|
||||
|
||||
Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode)
|
||||
QList<LaunchStep::Ptr> MinecraftInstance::createUpdateTask()
|
||||
{
|
||||
updateRuntimeContext();
|
||||
switch (mode) {
|
||||
case Net::Mode::Offline: {
|
||||
return Task::Ptr(new MinecraftLoadAndCheck(this));
|
||||
}
|
||||
case Net::Mode::Online: {
|
||||
return Task::Ptr(new MinecraftUpdate(this));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return {
|
||||
// create folders
|
||||
makeShared<FoldersTask>(this),
|
||||
// libraries download
|
||||
makeShared<LibrariesTask>(this),
|
||||
// FML libraries download and copy into the instance
|
||||
makeShared<FMLLibrariesTask>(this),
|
||||
// assets update
|
||||
makeShared<AssetUpdateTask>(this),
|
||||
};
|
||||
}
|
||||
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
@ -1090,14 +1090,10 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
process->appendStep(step);
|
||||
}
|
||||
|
||||
// if we aren't in offline mode,.
|
||||
if (session->status != AuthSession::PlayableOffline) {
|
||||
if (!session->demo) {
|
||||
process->appendStep(makeShared<ClaimAccount>(pptr, session));
|
||||
}
|
||||
process->appendStep(makeShared<Update>(pptr, Net::Mode::Online));
|
||||
} else {
|
||||
process->appendStep(makeShared<Update>(pptr, Net::Mode::Offline));
|
||||
// load meta
|
||||
{
|
||||
auto mode = session->status != AuthSession::PlayableOffline ? Net::Mode::Online : Net::Mode::Offline;
|
||||
process->appendStep(makeShared<TaskStepWrapper>(pptr, makeShared<MinecraftLoadAndCheck>(this, mode, pptr)));
|
||||
}
|
||||
|
||||
// check java
|
||||
@ -1106,6 +1102,16 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
process->appendStep(makeShared<CheckJava>(pptr));
|
||||
}
|
||||
|
||||
// if we aren't in offline mode,.
|
||||
if (session->status != AuthSession::PlayableOffline) {
|
||||
if (!session->demo) {
|
||||
process->appendStep(makeShared<ClaimAccount>(pptr, session));
|
||||
}
|
||||
for (auto t : createUpdateTask()) {
|
||||
process->appendStep(makeShared<TaskStepWrapper>(pptr, t));
|
||||
}
|
||||
}
|
||||
|
||||
// if there are any jar mods
|
||||
{
|
||||
process->appendStep(makeShared<ModMinecraftJar>(pptr));
|
||||
|
@ -104,7 +104,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
/** Returns whether the instance, with its version, has support for demo mode. */
|
||||
[[nodiscard]] bool supportsDemo() const;
|
||||
|
||||
void updateRuntimeContext();
|
||||
void updateRuntimeContext() override;
|
||||
|
||||
////// Profile management //////
|
||||
std::shared_ptr<PackProfile> getPackProfile() const;
|
||||
@ -120,7 +120,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
std::shared_ptr<GameOptions> gameOptionsModel();
|
||||
|
||||
////// Launch stuff //////
|
||||
Task::Ptr createUpdateTask(Net::Mode mode) override;
|
||||
QList<Task::Ptr> createUpdateTask() override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) override;
|
||||
QStringList extraArguments() override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override;
|
||||
|
@ -2,41 +2,42 @@
|
||||
#include "MinecraftInstance.h"
|
||||
#include "PackProfile.h"
|
||||
|
||||
MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance* inst, QObject* parent) : Task(parent), m_inst(inst) {}
|
||||
MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance* inst, Net::Mode netmode, QObject* parent)
|
||||
: Task(parent), m_inst(inst), m_netmode(netmode)
|
||||
{}
|
||||
|
||||
void MinecraftLoadAndCheck::executeTask()
|
||||
{
|
||||
// add offline metadata load task
|
||||
auto components = m_inst->getPackProfile();
|
||||
components->reload(Net::Mode::Offline);
|
||||
components->reload(m_netmode);
|
||||
m_task = components->getCurrentTask();
|
||||
|
||||
if (!m_task) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded);
|
||||
connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
|
||||
connect(m_task.get(), &Task::aborted, this, [this] { subtaskFailed(tr("Aborted")); });
|
||||
connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
|
||||
connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::emitSucceeded);
|
||||
connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::emitFailed);
|
||||
connect(m_task.get(), &Task::aborted, this, [this] { emitFailed(tr("Aborted")); });
|
||||
connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::setProgress);
|
||||
connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propagateStepProgress);
|
||||
connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
|
||||
connect(m_task.get(), &Task::details, this, &MinecraftLoadAndCheck::setDetails);
|
||||
}
|
||||
|
||||
void MinecraftLoadAndCheck::subtaskSucceeded()
|
||||
bool MinecraftLoadAndCheck::canAbort() const
|
||||
{
|
||||
if (isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
|
||||
return;
|
||||
if (m_task) {
|
||||
return m_task->canAbort();
|
||||
}
|
||||
emitSucceeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MinecraftLoadAndCheck::subtaskFailed(QString error)
|
||||
bool MinecraftLoadAndCheck::abort()
|
||||
{
|
||||
if (isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
|
||||
return;
|
||||
if (m_task && m_task->canAbort()) {
|
||||
return m_task->abort();
|
||||
}
|
||||
emitFailed(error);
|
||||
}
|
||||
return Task::abort();
|
||||
}
|
@ -15,32 +15,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include <quazip/quazip.h>
|
||||
#include "net/Mode.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
class MinecraftVersion;
|
||||
class MinecraftInstance;
|
||||
|
||||
class MinecraftLoadAndCheck : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MinecraftLoadAndCheck(MinecraftInstance* inst, QObject* parent = 0);
|
||||
virtual ~MinecraftLoadAndCheck() {};
|
||||
explicit MinecraftLoadAndCheck(MinecraftInstance* inst, Net::Mode netmode, QObject* parent = nullptr);
|
||||
virtual ~MinecraftLoadAndCheck() = default;
|
||||
void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void subtaskSucceeded();
|
||||
void subtaskFailed(QString error);
|
||||
bool canAbort() const override;
|
||||
public slots:
|
||||
bool abort() override;
|
||||
|
||||
private:
|
||||
MinecraftInstance* m_inst = nullptr;
|
||||
Task::Ptr m_task;
|
||||
QString m_preFailure;
|
||||
QString m_fail_reason;
|
||||
Net::Mode m_netmode;
|
||||
};
|
||||
|
@ -1,63 +0,0 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MinecraftUpdate.h"
|
||||
#include "MinecraftInstance.h"
|
||||
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "update/AssetUpdateTask.h"
|
||||
#include "update/FMLLibrariesTask.h"
|
||||
#include "update/FoldersTask.h"
|
||||
#include "update/LibrariesTask.h"
|
||||
|
||||
MinecraftUpdate::MinecraftUpdate(MinecraftInstance* inst, QObject* parent) : SequentialTask(parent), m_inst(inst) {}
|
||||
|
||||
void MinecraftUpdate::executeTask()
|
||||
{
|
||||
m_queue.clear();
|
||||
// create folders
|
||||
{
|
||||
addTask(makeShared<FoldersTask>(m_inst));
|
||||
}
|
||||
|
||||
// add metadata update task if necessary
|
||||
{
|
||||
auto components = m_inst->getPackProfile();
|
||||
components->reload(Net::Mode::Online);
|
||||
auto task = components->getCurrentTask();
|
||||
if (task) {
|
||||
addTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
// libraries download
|
||||
{
|
||||
addTask(makeShared<LibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// FML libraries download and copy into the instance
|
||||
{
|
||||
addTask(makeShared<FMLLibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// assets update
|
||||
{
|
||||
addTask(makeShared<AssetUpdateTask>(m_inst));
|
||||
}
|
||||
|
||||
SequentialTask::executeTask();
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
|
||||
// this needs to be a task because components->reload does stuff that may block
|
||||
class MinecraftUpdate : public SequentialTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MinecraftUpdate(MinecraftInstance* inst, QObject* parent = 0);
|
||||
virtual ~MinecraftUpdate() = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
private:
|
||||
MinecraftInstance* m_inst = nullptr;
|
||||
};
|
@ -38,6 +38,7 @@
|
||||
*/
|
||||
|
||||
#include <Version.h>
|
||||
#include <qlogging.h>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
@ -47,10 +48,16 @@
|
||||
#include <QSaveFile>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Exception.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/JsonFormat.h"
|
||||
#include "minecraft/Component.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/ProfileUtils.h"
|
||||
@ -60,11 +67,9 @@
|
||||
#include "PackProfile_p.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
|
||||
{ "net.minecraftforge", ModPlatform::Forge },
|
||||
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
|
||||
{ "org.quiltmc.quilt-loader", ModPlatform::Quilt },
|
||||
{ "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
|
||||
#include "minecraft/Logging.h"
|
||||
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
|
||||
{
|
||||
@ -153,16 +158,16 @@ static bool savePackProfile(const QString& filename, const ComponentContainer& c
|
||||
obj.insert("components", orderArray);
|
||||
QSaveFile outFile(filename);
|
||||
if (!outFile.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Couldn't open" << outFile.fileName() << "for writing:" << outFile.errorString();
|
||||
qCCritical(instanceProfileC) << "Couldn't open" << outFile.fileName() << "for writing:" << outFile.errorString();
|
||||
return false;
|
||||
}
|
||||
auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented);
|
||||
if (outFile.write(data) != data.size()) {
|
||||
qCritical() << "Couldn't write all the data into" << outFile.fileName() << "because:" << outFile.errorString();
|
||||
qCCritical(instanceProfileC) << "Couldn't write all the data into" << outFile.fileName() << "because:" << outFile.errorString();
|
||||
return false;
|
||||
}
|
||||
if (!outFile.commit()) {
|
||||
qCritical() << "Couldn't save" << outFile.fileName() << "because:" << outFile.errorString();
|
||||
qCCritical(instanceProfileC) << "Couldn't save" << outFile.fileName() << "because:" << outFile.errorString();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -175,12 +180,12 @@ static bool loadPackProfile(PackProfile* parent,
|
||||
{
|
||||
QFile componentsFile(filename);
|
||||
if (!componentsFile.exists()) {
|
||||
qWarning() << "Components file doesn't exist. This should never happen.";
|
||||
qCWarning(instanceProfileC) << "Components file" << filename << "doesn't exist. This should never happen.";
|
||||
return false;
|
||||
}
|
||||
if (!componentsFile.open(QFile::ReadOnly)) {
|
||||
qCritical() << "Couldn't open" << componentsFile.fileName() << " for reading:" << componentsFile.errorString();
|
||||
qWarning() << "Ignoring overridden order";
|
||||
qCCritical(instanceProfileC) << "Couldn't open" << componentsFile.fileName() << " for reading:" << componentsFile.errorString();
|
||||
qCWarning(instanceProfileC) << "Ignoring overridden order";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -188,8 +193,8 @@ static bool loadPackProfile(PackProfile* parent,
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCritical() << "Couldn't parse" << componentsFile.fileName() << ":" << error.errorString();
|
||||
qWarning() << "Ignoring overridden order";
|
||||
qCCritical(instanceProfileC) << "Couldn't parse" << componentsFile.fileName() << ":" << error.errorString();
|
||||
qCWarning(instanceProfileC) << "Ignoring overridden order";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -207,7 +212,7 @@ static bool loadPackProfile(PackProfile* parent,
|
||||
container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj));
|
||||
}
|
||||
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||
qCritical() << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
||||
qCCritical(instanceProfileC) << "Couldn't parse" << componentsFile.fileName() << ": bad file format";
|
||||
container.clear();
|
||||
return false;
|
||||
}
|
||||
@ -240,12 +245,12 @@ void PackProfile::buildingFromScratch()
|
||||
void PackProfile::scheduleSave()
|
||||
{
|
||||
if (!d->loaded) {
|
||||
qDebug() << "Component list should never save if it didn't successfully load, instance:" << d->m_instance->name();
|
||||
qDebug() << d->m_instance->name() << "|" << "Component list should never save if it didn't successfully load";
|
||||
return;
|
||||
}
|
||||
if (!d->dirty) {
|
||||
d->dirty = true;
|
||||
qDebug() << "Component list save is scheduled for" << d->m_instance->name();
|
||||
qDebug() << d->m_instance->name() << "|" << "Component list save is scheduled";
|
||||
}
|
||||
d->m_saveTimer.start();
|
||||
}
|
||||
@ -272,7 +277,7 @@ QString PackProfile::patchFilePathForUid(const QString& uid) const
|
||||
|
||||
void PackProfile::save_internal()
|
||||
{
|
||||
qDebug() << "Component list save performed now for" << d->m_instance->name();
|
||||
qDebug() << d->m_instance->name() << "|" << "Component list save performed now";
|
||||
auto filename = componentsFilePath();
|
||||
savePackProfile(filename, d->components);
|
||||
d->dirty = false;
|
||||
@ -285,7 +290,7 @@ bool PackProfile::load()
|
||||
// load the new component list and swap it with the current one...
|
||||
ComponentContainer newComponents;
|
||||
if (!loadPackProfile(this, filename, patchesPattern(), newComponents)) {
|
||||
qCritical() << "Failed to load the component config for instance" << d->m_instance->name();
|
||||
qCritical() << d->m_instance->name() << "|" << "Failed to load the component config";
|
||||
return false;
|
||||
} else {
|
||||
// FIXME: actually use fine-grained updates, not this...
|
||||
@ -298,7 +303,7 @@ bool PackProfile::load()
|
||||
d->componentIndex.clear();
|
||||
for (auto component : newComponents) {
|
||||
if (d->componentIndex.contains(component->m_uid)) {
|
||||
qWarning() << "Ignoring duplicate component entry" << component->m_uid;
|
||||
qWarning() << d->m_instance->name() << "|" << "Ignoring duplicate component entry" << component->m_uid;
|
||||
continue;
|
||||
}
|
||||
connect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged);
|
||||
@ -346,14 +351,14 @@ void PackProfile::resolve(Net::Mode netmode)
|
||||
|
||||
void PackProfile::updateSucceeded()
|
||||
{
|
||||
qDebug() << "Component list update/resolve task succeeded for" << d->m_instance->name();
|
||||
qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task succeeded";
|
||||
d->m_updateTask.reset();
|
||||
invalidateLaunchProfile();
|
||||
}
|
||||
|
||||
void PackProfile::updateFailed(const QString& error)
|
||||
{
|
||||
qDebug() << "Component list update/resolve task failed for" << d->m_instance->name() << "Reason:" << error;
|
||||
qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task failed " << "Reason:" << error;
|
||||
d->m_updateTask.reset();
|
||||
invalidateLaunchProfile();
|
||||
}
|
||||
@ -369,11 +374,11 @@ void PackProfile::insertComponent(size_t index, ComponentPtr component)
|
||||
{
|
||||
auto id = component->getID();
|
||||
if (id.isEmpty()) {
|
||||
qWarning() << "Attempt to add a component with empty ID!";
|
||||
qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Attempt to add a component with empty ID!";
|
||||
return;
|
||||
}
|
||||
if (d->componentIndex.contains(id)) {
|
||||
qWarning() << "Attempt to add a component that is already present!";
|
||||
qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Attempt to add a component that is already present!";
|
||||
return;
|
||||
}
|
||||
beginInsertRows(QModelIndex(), static_cast<int>(index), static_cast<int>(index));
|
||||
@ -388,7 +393,7 @@ void PackProfile::componentDataChanged()
|
||||
{
|
||||
auto objPtr = qobject_cast<Component*>(sender());
|
||||
if (!objPtr) {
|
||||
qWarning() << "PackProfile got dataChanged signal from a non-Component!";
|
||||
qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "PackProfile got dataChanged signal from a non-Component!";
|
||||
return;
|
||||
}
|
||||
if (objPtr->getID() == "net.minecraft") {
|
||||
@ -404,19 +409,20 @@ void PackProfile::componentDataChanged()
|
||||
}
|
||||
index++;
|
||||
}
|
||||
qWarning() << "PackProfile got dataChanged signal from a Component which does not belong to it!";
|
||||
qCWarning(instanceProfileC) << d->m_instance->name() << "|"
|
||||
<< "PackProfile got dataChanged signal from a Component which does not belong to it!";
|
||||
}
|
||||
|
||||
bool PackProfile::remove(const int index)
|
||||
{
|
||||
auto patch = getComponent(index);
|
||||
if (!patch->isRemovable()) {
|
||||
qWarning() << "Patch" << patch->getID() << "is non-removable";
|
||||
qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "is non-removable";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!removeComponent_internal(patch)) {
|
||||
qCritical() << "Patch" << patch->getID() << "could not be removed";
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "could not be removed";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -445,11 +451,11 @@ bool PackProfile::customize(int index)
|
||||
{
|
||||
auto patch = getComponent(index);
|
||||
if (!patch->isCustomizable()) {
|
||||
qDebug() << "Patch" << patch->getID() << "is not customizable";
|
||||
qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "is not customizable";
|
||||
return false;
|
||||
}
|
||||
if (!patch->customize()) {
|
||||
qCritical() << "Patch" << patch->getID() << "could not be customized";
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "could not be customized";
|
||||
return false;
|
||||
}
|
||||
invalidateLaunchProfile();
|
||||
@ -461,11 +467,11 @@ bool PackProfile::revertToBase(int index)
|
||||
{
|
||||
auto patch = getComponent(index);
|
||||
if (!patch->isRevertible()) {
|
||||
qDebug() << "Patch" << patch->getID() << "is not revertible";
|
||||
qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "is not revertible";
|
||||
return false;
|
||||
}
|
||||
if (!patch->revert()) {
|
||||
qCritical() << "Patch" << patch->getID() << "could not be reverted";
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "could not be reverted";
|
||||
return false;
|
||||
}
|
||||
invalidateLaunchProfile();
|
||||
@ -678,7 +684,8 @@ bool PackProfile::installComponents(QStringList selectedFiles)
|
||||
const QString target = FS::PathCombine(patchDir, versionFile->uid + ".json");
|
||||
|
||||
if (!QFile::copy(source, target)) {
|
||||
qWarning() << "Component" << source << "could not be copied to target" << target;
|
||||
qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Component" << source << "could not be copied to target"
|
||||
<< target;
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
@ -711,7 +718,8 @@ bool PackProfile::installEmpty(const QString& uid, const QString& name)
|
||||
QString patchFileName = FS::PathCombine(patchDir, uid + ".json");
|
||||
QFile file(patchFileName);
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << file.fileName()
|
||||
<< "for reading:" << file.errorString();
|
||||
return false;
|
||||
}
|
||||
file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
|
||||
@ -731,7 +739,8 @@ bool PackProfile::removeComponent_internal(ComponentPtr patch)
|
||||
if (fileName.size()) {
|
||||
QFile patchFile(fileName);
|
||||
if (patchFile.exists() && !patchFile.remove()) {
|
||||
qCritical() << "File" << fileName << "could not be removed because:" << patchFile.errorString();
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "File" << fileName
|
||||
<< "could not be removed because:" << patchFile.errorString();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -747,7 +756,8 @@ bool PackProfile::removeComponent_internal(ComponentPtr patch)
|
||||
if (finfo.exists()) {
|
||||
QFile jarModFile(jar[0]);
|
||||
if (!jarModFile.remove()) {
|
||||
qCritical() << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString();
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "File" << jar[0]
|
||||
<< "could not be removed because:" << jarModFile.errorString();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -804,7 +814,8 @@ bool PackProfile::installJarMods_internal(QStringList filepaths)
|
||||
|
||||
QFile file(patchFileName);
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << file.fileName()
|
||||
<< "for reading:" << file.errorString();
|
||||
return false;
|
||||
}
|
||||
file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
|
||||
@ -858,7 +869,8 @@ bool PackProfile::installCustomJar_internal(QString filepath)
|
||||
|
||||
QFile file(patchFileName);
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << file.fileName()
|
||||
<< "for reading:" << file.errorString();
|
||||
return false;
|
||||
}
|
||||
file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
|
||||
@ -913,7 +925,8 @@ bool PackProfile::installAgents_internal(QStringList filepaths)
|
||||
QFile patchFile(FS::PathCombine(patchDir, targetId + ".json"));
|
||||
|
||||
if (!patchFile.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Error opening" << patchFile.fileName() << "for reading:" << patchFile.errorString();
|
||||
qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << patchFile.fileName()
|
||||
<< "for reading:" << patchFile.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -935,12 +948,13 @@ std::shared_ptr<LaunchProfile> PackProfile::getProfile() const
|
||||
try {
|
||||
auto profile = std::make_shared<LaunchProfile>();
|
||||
for (auto file : d->components) {
|
||||
qDebug() << "Applying" << file->getID() << (file->getProblemSeverity() == ProblemSeverity::Error ? "ERROR" : "GOOD");
|
||||
qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Applying" << file->getID()
|
||||
<< (file->getProblemSeverity() == ProblemSeverity::Error ? "ERROR" : "GOOD");
|
||||
file->applyTo(profile.get());
|
||||
}
|
||||
d->m_profile = profile;
|
||||
} catch (const Exception& error) {
|
||||
qWarning() << "Couldn't apply profile patches because: " << error.cause();
|
||||
qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Couldn't apply profile patches because: " << error.cause();
|
||||
}
|
||||
}
|
||||
return d->m_profile;
|
||||
@ -953,8 +967,16 @@ bool PackProfile::setComponentVersion(const QString& uid, const QString& version
|
||||
ComponentPtr component = *iter;
|
||||
// set existing
|
||||
if (component->revert()) {
|
||||
// set new version
|
||||
auto oldVersion = component->getVersion();
|
||||
component->setVersion(version);
|
||||
component->setImportant(important);
|
||||
|
||||
if (important) {
|
||||
component->setUpdateAction(UpdateAction{ UpdateActionImportantChanged{ oldVersion } });
|
||||
resolve(Net::Mode::Online);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -993,12 +1015,12 @@ std::optional<ModPlatform::ModLoaderTypes> PackProfile::getModLoaders()
|
||||
ModPlatform::ModLoaderTypes result;
|
||||
bool has_any_loader = false;
|
||||
|
||||
QMapIterator<QString, ModPlatform::ModLoaderType> i(modloaderMapping);
|
||||
QMapIterator<QString, ModloaderMapEntry> i(Component::KNOWN_MODLOADERS);
|
||||
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (auto c = getComponent(i.key()); c != nullptr && c->isEnabled()) {
|
||||
result |= i.value();
|
||||
result |= i.value().type;
|
||||
has_any_loader = true;
|
||||
}
|
||||
}
|
||||
@ -1026,8 +1048,8 @@ QList<ModPlatform::ModLoaderType> PackProfile::getModLoadersList()
|
||||
{
|
||||
QList<ModPlatform::ModLoaderType> result;
|
||||
for (auto c : d->components) {
|
||||
if (c->isEnabled() && modloaderMapping.contains(c->getID())) {
|
||||
result.append(modloaderMapping[c->getID()]);
|
||||
if (c->isEnabled() && Component::KNOWN_MODLOADERS.contains(c->getID())) {
|
||||
result.append(Component::KNOWN_MODLOADERS[c->getID()].type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,13 +148,13 @@ class PackProfile : public QAbstractListModel {
|
||||
std::optional<ModPlatform::ModLoaderTypes> getSupportedModLoaders();
|
||||
QList<ModPlatform::ModLoaderType> getModLoadersList();
|
||||
|
||||
/// apply the component patches. Catches all the errors and returns true/false for success/failure
|
||||
void invalidateLaunchProfile();
|
||||
|
||||
private:
|
||||
void scheduleSave();
|
||||
bool saveIsScheduled() const;
|
||||
|
||||
/// apply the component patches. Catches all the errors and returns true/false for success/failure
|
||||
void invalidateLaunchProfile();
|
||||
|
||||
/// insert component so that its index is ideally the specified one (returns real index)
|
||||
void insertComponent(size_t index, ComponentPtr component);
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QTimer>
|
||||
#include <map>
|
||||
#include "Component.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
using ComponentContainer = QList<ComponentPtr>;
|
||||
|
@ -37,7 +37,6 @@
|
||||
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <launch/LaunchTask.h>
|
||||
#include "java/JavaMetadata.h"
|
||||
#include "meta/Version.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "tasks/Task.h"
|
||||
|
@ -22,7 +22,7 @@ class ClaimAccount : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ClaimAccount(LaunchTask* parent, AuthSessionPtr session);
|
||||
virtual ~ClaimAccount() {};
|
||||
virtual ~ClaimAccount() = default;
|
||||
|
||||
void executeTask() override;
|
||||
void finalize() override;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "AssetUpdateTask.h"
|
||||
|
||||
#include "launch/LaunchStep.h"
|
||||
#include "minecraft/AssetsUtils.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
@ -14,8 +15,6 @@ AssetUpdateTask::AssetUpdateTask(MinecraftInstance* inst)
|
||||
m_inst = inst;
|
||||
}
|
||||
|
||||
AssetUpdateTask::~AssetUpdateTask() {}
|
||||
|
||||
void AssetUpdateTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Updating assets index..."));
|
||||
|
@ -7,7 +7,7 @@ class AssetUpdateTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssetUpdateTask(MinecraftInstance* inst);
|
||||
virtual ~AssetUpdateTask();
|
||||
virtual ~AssetUpdateTask() = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
@ -9,7 +9,7 @@ class FMLLibrariesTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FMLLibrariesTask(MinecraftInstance* inst);
|
||||
virtual ~FMLLibrariesTask() {};
|
||||
virtual ~FMLLibrariesTask() = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include <QDir>
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
FoldersTask::FoldersTask(MinecraftInstance* inst) : Task()
|
||||
FoldersTask::FoldersTask(MinecraftInstance* inst)
|
||||
{
|
||||
m_inst = inst;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ class FoldersTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FoldersTask(MinecraftInstance* inst);
|
||||
virtual ~FoldersTask() {};
|
||||
virtual ~FoldersTask() = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
@ -7,7 +7,7 @@ class LibrariesTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LibrariesTask(MinecraftInstance* inst);
|
||||
virtual ~LibrariesTask() {};
|
||||
virtual ~LibrariesTask() = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
@ -242,6 +242,11 @@ ScreenshotsPage::ScreenshotsPage(QString path, QWidget* parent) : QMainWindow(pa
|
||||
m_model->setReadOnly(false);
|
||||
m_model->setNameFilters({ "*.png" });
|
||||
m_model->setNameFilterDisables(false);
|
||||
// Sorts by modified date instead of creation date because that column is not available and would require subclassing, this should work
|
||||
// considering screenshots aren't modified after creation.
|
||||
constexpr int file_modified_column_index = 3;
|
||||
m_model->sort(file_modified_column_index, Qt::DescendingOrder);
|
||||
|
||||
m_folder = path;
|
||||
m_valid = FS::ensureFolderPathExists(m_folder);
|
||||
|
||||
|
@ -49,8 +49,12 @@
|
||||
#include <QMessageBox>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <algorithm>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
#include "VersionPage.h"
|
||||
#include "meta/JsonFormat.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "ui/dialogs/InstallLoaderDialog.h"
|
||||
#include "ui_VersionPage.h"
|
||||
|
||||
@ -63,11 +67,9 @@
|
||||
|
||||
#include "DesktopServices.h"
|
||||
#include "Exception.h"
|
||||
#include "Version.h"
|
||||
#include "icons/IconList.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/auth/AccountList.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
|
||||
#include "meta/Index.h"
|
||||
#include "meta/VersionList.h"
|
||||
@ -370,11 +372,25 @@ void VersionPage::on_actionChange_version_triggered()
|
||||
auto patch = m_profile->getComponent(versionRow);
|
||||
auto name = patch->getName();
|
||||
auto list = patch->getVersionList();
|
||||
list->clearExternalRecommends();
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
auto uid = list->uid();
|
||||
|
||||
// recommend the correct lwjgl version for the current minecraft version
|
||||
if (uid == "org.lwjgl" || uid == "org.lwjgl3") {
|
||||
auto minecraft = m_profile->getComponent("net.minecraft");
|
||||
auto lwjglReq = std::find_if(minecraft->m_cachedRequires.cbegin(), minecraft->m_cachedRequires.cend(),
|
||||
[uid](const Meta::Require& req) -> bool { return req.uid == uid; });
|
||||
if (lwjglReq != minecraft->m_cachedRequires.cend()) {
|
||||
auto lwjglVersion = !lwjglReq->equalsVersion.isEmpty() ? lwjglReq->equalsVersion : lwjglReq->suggests;
|
||||
if (!lwjglVersion.isEmpty()) {
|
||||
list->addExternalRecommends({ lwjglVersion });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this);
|
||||
if (uid == "net.fabricmc.intermediary" || uid == "org.quiltmc.hashed") {
|
||||
vselect.setEmptyString(tr("No intermediary mappings versions are currently available."));
|
||||
@ -415,14 +431,18 @@ void VersionPage::on_actionDownload_All_triggered()
|
||||
return;
|
||||
}
|
||||
|
||||
auto updateTask = m_inst->createUpdateTask(Net::Mode::Online);
|
||||
if (!updateTask) {
|
||||
auto updateTasks = m_inst->createUpdateTask();
|
||||
if (updateTasks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto task = makeShared<SequentialTask>(this);
|
||||
for (auto t : updateTasks) {
|
||||
task->addTask(t);
|
||||
}
|
||||
ProgressDialog tDialog(this);
|
||||
connect(updateTask.get(), &Task::failed, this, &VersionPage::onGameUpdateError);
|
||||
connect(task.get(), &Task::failed, this, &VersionPage::onGameUpdateError);
|
||||
// FIXME: unused return value
|
||||
tDialog.execWithTask(updateTask.get());
|
||||
tDialog.execWithTask(task.get());
|
||||
updateButtons();
|
||||
m_container->refreshContainer();
|
||||
}
|
||||
|
33
launcher/ui/setupwizard/AutoJavaWizardPage.cpp
Normal file
33
launcher/ui/setupwizard/AutoJavaWizardPage.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "AutoJavaWizardPage.h"
|
||||
#include "ui_AutoJavaWizardPage.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
AutoJavaWizardPage::AutoJavaWizardPage(QWidget* parent) : BaseWizardPage(parent), ui(new Ui::AutoJavaWizardPage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
AutoJavaWizardPage::~AutoJavaWizardPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AutoJavaWizardPage::initializePage() {}
|
||||
|
||||
bool AutoJavaWizardPage::validatePage()
|
||||
{
|
||||
auto s = APPLICATION->settings();
|
||||
|
||||
if (!ui->previousSettingsRadioButton->isChecked()) {
|
||||
s->set("AutomaticJavaSwitch", true);
|
||||
s->set("AutomaticJavaDownload", true);
|
||||
}
|
||||
s->set("UserAskedAboutAutomaticJavaDownload", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoJavaWizardPage::retranslate()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
22
launcher/ui/setupwizard/AutoJavaWizardPage.h
Normal file
22
launcher/ui/setupwizard/AutoJavaWizardPage.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include "BaseWizardPage.h"
|
||||
|
||||
namespace Ui {
|
||||
class AutoJavaWizardPage;
|
||||
}
|
||||
|
||||
class AutoJavaWizardPage : public BaseWizardPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AutoJavaWizardPage(QWidget* parent = nullptr);
|
||||
~AutoJavaWizardPage();
|
||||
|
||||
void initializePage() override;
|
||||
bool validatePage() override;
|
||||
void retranslate() override;
|
||||
|
||||
private:
|
||||
Ui::AutoJavaWizardPage* ui;
|
||||
};
|
93
launcher/ui/setupwizard/AutoJavaWizardPage.ui
Normal file
93
launcher/ui/setupwizard/AutoJavaWizardPage.ui
Normal file
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AutoJavaWizardPage</class>
|
||||
<widget class="QWidget" name="AutoJavaWizardPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">New Feature Alert!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>We've added a feature to automatically download the correct Java version for each version of Minecraft(this can be changed in the Java Settings). Would you like to enable or disable this feature?</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="defaultSettingsRadioButton">
|
||||
<property name="text">
|
||||
<string>Enable Auto-Download</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="previousSettingsRadioButton">
|
||||
<property name="text">
|
||||
<string>Disable Auto-Download</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>156</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="buttonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
@ -55,6 +55,7 @@ bool JavaWizardPage::validatePage()
|
||||
auto result = m_java_widget->validate();
|
||||
settings->set("AutomaticJavaSwitch", m_java_widget->autoDetectJava());
|
||||
settings->set("AutomaticJavaDownload", m_java_widget->autoDownloadJava());
|
||||
settings->set("UserAskedAboutAutomaticJavaDownload", true);
|
||||
switch (result) {
|
||||
default:
|
||||
case JavaSettingsWidget::ValidationStatus::Bad: {
|
||||
@ -82,7 +83,6 @@ void JavaWizardPage::retranslate()
|
||||
{
|
||||
setTitle(tr("Java"));
|
||||
setSubTitle(
|
||||
tr("You do not have a working Java set up yet or it went missing.\n"
|
||||
"Please select one of the following or browse for a Java executable."));
|
||||
tr("Please select how much memory to allocate to instances and if Prism Launcher should manage java automatically or manually."));
|
||||
m_java_widget->retranslate();
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class JavaWizardPage : public BaseWizardPage {
|
||||
public:
|
||||
explicit JavaWizardPage(QWidget* parent = Q_NULLPTR);
|
||||
|
||||
virtual ~JavaWizardPage() {};
|
||||
virtual ~JavaWizardPage() = default;
|
||||
|
||||
bool wantsRefreshButton() override;
|
||||
void refresh() override;
|
||||
|
45
launcher/ui/setupwizard/LoginWizardPage.cpp
Normal file
45
launcher/ui/setupwizard/LoginWizardPage.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "LoginWizardPage.h"
|
||||
#include "minecraft/auth/AccountList.h"
|
||||
#include "ui/dialogs/MSALoginDialog.h"
|
||||
#include "ui_LoginWizardPage.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
LoginWizardPage::LoginWizardPage(QWidget* parent) : BaseWizardPage(parent), ui(new Ui::LoginWizardPage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
LoginWizardPage::~LoginWizardPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void LoginWizardPage::initializePage() {}
|
||||
|
||||
bool LoginWizardPage::validatePage()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoginWizardPage::retranslate()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void LoginWizardPage::on_pushButton_clicked()
|
||||
{
|
||||
wizard()->hide();
|
||||
auto account = MSALoginDialog::newAccount(nullptr);
|
||||
wizard()->show();
|
||||
if (account) {
|
||||
APPLICATION->accounts()->addAccount(account);
|
||||
APPLICATION->accounts()->setDefaultAccount(account);
|
||||
}
|
||||
|
||||
if (wizard()->currentId() == wizard()->pageIds().last()) {
|
||||
wizard()->accept();
|
||||
} else {
|
||||
wizard()->next();
|
||||
}
|
||||
}
|
24
launcher/ui/setupwizard/LoginWizardPage.h
Normal file
24
launcher/ui/setupwizard/LoginWizardPage.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include "BaseWizardPage.h"
|
||||
|
||||
namespace Ui {
|
||||
class LoginWizardPage;
|
||||
}
|
||||
|
||||
class LoginWizardPage : public BaseWizardPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LoginWizardPage(QWidget* parent = nullptr);
|
||||
~LoginWizardPage();
|
||||
|
||||
void initializePage() override;
|
||||
bool validatePage() override;
|
||||
void retranslate() override;
|
||||
private slots:
|
||||
void on_pushButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::LoginWizardPage* ui;
|
||||
};
|
74
launcher/ui/setupwizard/LoginWizardPage.ui
Normal file
74
launcher/ui/setupwizard/LoginWizardPage.ui
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LoginWizardPage</class>
|
||||
<widget class="QWidget" name="LoginWizardPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Add Microsoft account</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>In order to play Minecraft, you must have at least one Microsoft account logged in. Do you want to log in now?</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Add Microsoft account</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>156</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="buttonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
@ -3,9 +3,11 @@
|
||||
#include <QFileDialog>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QLayoutItem>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QSizePolicy>
|
||||
#include <QSpinBox>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
@ -34,11 +36,13 @@ JavaSettingsWidget::JavaSettingsWidget(QWidget* parent) : QWidget(parent)
|
||||
goodIcon = APPLICATION->getThemedIcon("status-good");
|
||||
yellowIcon = APPLICATION->getThemedIcon("status-yellow");
|
||||
badIcon = APPLICATION->getThemedIcon("status-bad");
|
||||
m_memoryTimer = new QTimer(this);
|
||||
setupUi();
|
||||
|
||||
connect(m_minMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int)));
|
||||
connect(m_maxMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int)));
|
||||
connect(m_permGenSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int)));
|
||||
connect(m_minMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxValueChanged(int)));
|
||||
connect(m_maxMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxValueChanged(int)));
|
||||
connect(m_permGenSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxValueChanged(int)));
|
||||
connect(m_memoryTimer, &QTimer::timeout, this, &JavaSettingsWidget::memoryValueChanged);
|
||||
connect(m_versionWidget, &VersionSelectWidget::selectedVersionChanged, this, &JavaSettingsWidget::javaVersionSelected);
|
||||
connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaBrowseBtn_clicked);
|
||||
connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited);
|
||||
@ -55,7 +59,6 @@ void JavaSettingsWidget::setupUi()
|
||||
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
|
||||
m_versionWidget = new VersionSelectWidget(this);
|
||||
m_verticalLayout->addWidget(m_versionWidget);
|
||||
|
||||
m_horizontalLayout = new QHBoxLayout();
|
||||
m_horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
|
||||
@ -73,8 +76,6 @@ void JavaSettingsWidget::setupUi()
|
||||
m_javaStatusBtn->setIcon(yellowIcon);
|
||||
m_horizontalLayout->addWidget(m_javaStatusBtn);
|
||||
|
||||
m_verticalLayout->addLayout(m_horizontalLayout);
|
||||
|
||||
m_memoryGroupBox = new QGroupBox(this);
|
||||
m_memoryGroupBox->setObjectName(QStringLiteral("memoryGroupBox"));
|
||||
m_gridLayout_2 = new QGridLayout(m_memoryGroupBox);
|
||||
@ -136,8 +137,6 @@ void JavaSettingsWidget::setupUi()
|
||||
m_horizontalBtnLayout->addWidget(m_javaDownloadBtn);
|
||||
}
|
||||
|
||||
m_verticalLayout->addLayout(m_horizontalBtnLayout);
|
||||
|
||||
m_autoJavaGroupBox = new QGroupBox(this);
|
||||
m_autoJavaGroupBox->setObjectName(QStringLiteral("autoJavaGroupBox"));
|
||||
m_veriticalJavaLayout = new QVBoxLayout(m_autoJavaGroupBox);
|
||||
@ -145,21 +144,42 @@ void JavaSettingsWidget::setupUi()
|
||||
|
||||
m_autodetectJavaCheckBox = new QCheckBox(m_autoJavaGroupBox);
|
||||
m_autodetectJavaCheckBox->setObjectName("autodetectJavaCheckBox");
|
||||
m_autodetectJavaCheckBox->setChecked(true);
|
||||
m_veriticalJavaLayout->addWidget(m_autodetectJavaCheckBox);
|
||||
|
||||
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
|
||||
m_autodownloadCheckBox = new QCheckBox(m_autoJavaGroupBox);
|
||||
m_autodownloadCheckBox->setObjectName("autodownloadCheckBox");
|
||||
m_autodownloadCheckBox->setEnabled(false);
|
||||
m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked());
|
||||
m_veriticalJavaLayout->addWidget(m_autodownloadCheckBox);
|
||||
connect(m_autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] {
|
||||
m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked());
|
||||
if (!m_autodetectJavaCheckBox->isChecked())
|
||||
m_autodownloadCheckBox->setChecked(false);
|
||||
});
|
||||
|
||||
connect(m_autodownloadCheckBox, &QCheckBox::stateChanged, this, [this] {
|
||||
auto isChecked = m_autodownloadCheckBox->isChecked();
|
||||
m_versionWidget->setVisible(!isChecked);
|
||||
m_javaStatusBtn->setVisible(!isChecked);
|
||||
m_javaBrowseBtn->setVisible(!isChecked);
|
||||
m_javaPathTextBox->setVisible(!isChecked);
|
||||
m_javaDownloadBtn->setVisible(!isChecked);
|
||||
if (!isChecked) {
|
||||
m_verticalLayout->removeItem(m_verticalSpacer);
|
||||
} else {
|
||||
m_verticalLayout->addSpacerItem(m_verticalSpacer);
|
||||
}
|
||||
});
|
||||
}
|
||||
m_verticalLayout->addWidget(m_autoJavaGroupBox);
|
||||
|
||||
m_verticalLayout->addLayout(m_horizontalBtnLayout);
|
||||
|
||||
m_verticalLayout->addWidget(m_versionWidget);
|
||||
m_verticalLayout->addLayout(m_horizontalLayout);
|
||||
m_verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
|
||||
retranslate();
|
||||
}
|
||||
|
||||
@ -177,23 +197,16 @@ void JavaSettingsWidget::initialize()
|
||||
m_maxMemSpinBox->setValue(observedMaxMemory);
|
||||
m_permGenSpinBox->setValue(observedPermGenMemory);
|
||||
updateThresholds();
|
||||
|
||||
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
|
||||
auto button =
|
||||
CustomMessageBox::selectable(this, tr("Automatic Java Download"),
|
||||
tr("%1 can automatically download the correct Java version for each version of Minecraft..\n"
|
||||
"Do you want to enable Java auto-download?\n")
|
||||
.arg(BuildConfig.LAUNCHER_DISPLAYNAME),
|
||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)
|
||||
->exec();
|
||||
auto checked = button == QMessageBox::Yes;
|
||||
m_autodetectJavaCheckBox->setChecked(checked);
|
||||
m_autodownloadCheckBox->setChecked(checked);
|
||||
m_autodownloadCheckBox->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::refresh()
|
||||
{
|
||||
if (BuildConfig.JAVA_DOWNLOADER_ENABLED && m_autodownloadCheckBox->isChecked()) {
|
||||
return;
|
||||
}
|
||||
if (JavaUtils::getJavaCheckPath().isEmpty()) {
|
||||
JavaCommon::javaCheckNotFound(this);
|
||||
return;
|
||||
@ -297,20 +310,21 @@ int JavaSettingsWidget::permGenSize() const
|
||||
return m_permGenSpinBox->value();
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::memoryValueChanged(int)
|
||||
void JavaSettingsWidget::memoryValueChanged()
|
||||
{
|
||||
bool actuallyChanged = false;
|
||||
unsigned int min = m_minMemSpinBox->value();
|
||||
unsigned int max = m_maxMemSpinBox->value();
|
||||
unsigned int permgen = m_permGenSpinBox->value();
|
||||
QObject* obj = sender();
|
||||
if (obj == m_minMemSpinBox && min != observedMinMemory) {
|
||||
if (min != observedMinMemory) {
|
||||
observedMinMemory = min;
|
||||
actuallyChanged = true;
|
||||
} else if (obj == m_maxMemSpinBox && max != observedMaxMemory) {
|
||||
}
|
||||
if (max != observedMaxMemory) {
|
||||
observedMaxMemory = max;
|
||||
actuallyChanged = true;
|
||||
} else if (obj == m_permGenSpinBox && permgen != observedPermGenMemory) {
|
||||
}
|
||||
if (permgen != observedPermGenMemory) {
|
||||
observedPermGenMemory = permgen;
|
||||
actuallyChanged = true;
|
||||
}
|
||||
@ -505,6 +519,9 @@ void JavaSettingsWidget::updateThresholds()
|
||||
} else if (observedMaxMemory < observedMinMemory) {
|
||||
iconName = "status-yellow";
|
||||
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value"));
|
||||
} else if (BuildConfig.JAVA_DOWNLOADER_ENABLED && m_autodownloadCheckBox->isChecked()) {
|
||||
iconName = "status-good";
|
||||
m_labelMaxMemIcon->setToolTip("");
|
||||
} else if (observedMaxMemory > 2048 && !m_result.is_64bit) {
|
||||
iconName = "status-bad";
|
||||
m_labelMaxMemIcon->setToolTip(tr("You are exceeding the maximum allocation supported by 32-bit installations of Java."));
|
||||
@ -530,3 +547,13 @@ bool JavaSettingsWidget::autoDetectJava() const
|
||||
{
|
||||
return m_autodetectJavaCheckBox->isChecked();
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::onSpinBoxValueChanged(int)
|
||||
{
|
||||
m_memoryTimer->start(500);
|
||||
}
|
||||
|
||||
JavaSettingsWidget::~JavaSettingsWidget()
|
||||
{
|
||||
delete m_verticalSpacer;
|
||||
};
|
@ -17,6 +17,7 @@ class QGroupBox;
|
||||
class QGridLayout;
|
||||
class QLabel;
|
||||
class QToolButton;
|
||||
class QSpacerItem;
|
||||
|
||||
/**
|
||||
* This is a widget for all the Java settings dialogs and pages.
|
||||
@ -26,7 +27,7 @@ class JavaSettingsWidget : public QWidget {
|
||||
|
||||
public:
|
||||
explicit JavaSettingsWidget(QWidget* parent);
|
||||
virtual ~JavaSettingsWidget() = default;
|
||||
virtual ~JavaSettingsWidget();
|
||||
|
||||
enum class JavaStatus { NotSet, Pending, Good, DoesNotExist, DoesNotStart, ReturnedInvalidData } javaStatus = JavaStatus::NotSet;
|
||||
|
||||
@ -48,7 +49,8 @@ class JavaSettingsWidget : public QWidget {
|
||||
void updateThresholds();
|
||||
|
||||
protected slots:
|
||||
void memoryValueChanged(int);
|
||||
void onSpinBoxValueChanged(int);
|
||||
void memoryValueChanged();
|
||||
void javaPathEdited(const QString& path);
|
||||
void javaVersionSelected(BaseVersion::Ptr version);
|
||||
void on_javaBrowseBtn_clicked();
|
||||
@ -65,6 +67,7 @@ class JavaSettingsWidget : public QWidget {
|
||||
private: /* data */
|
||||
VersionSelectWidget* m_versionWidget = nullptr;
|
||||
QVBoxLayout* m_verticalLayout = nullptr;
|
||||
QSpacerItem* m_verticalSpacer = nullptr;
|
||||
|
||||
QLineEdit* m_javaPathTextBox = nullptr;
|
||||
QPushButton* m_javaBrowseBtn = nullptr;
|
||||
@ -99,4 +102,5 @@ class JavaSettingsWidget : public QWidget {
|
||||
uint64_t m_availableMemory = 0ull;
|
||||
shared_qobject_ptr<JavaChecker> m_checker;
|
||||
JavaChecker::Result m_result;
|
||||
QTimer* m_memoryTimer;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user