Merge branch 'PrismLauncher:develop' into skinfix

This commit is contained in:
TheKodeToad
2023-08-04 16:29:30 +01:00
committed by GitHub
75 changed files with 1337 additions and 668 deletions

View File

@ -131,7 +131,7 @@
#include "MangoHud.h"
#endif
#ifdef Q_OS_MAC
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
#include "updater/MacSparkleUpdater.h"
#endif
@ -281,7 +281,16 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
}
else
{
QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
QDir foo;
if (DesktopServices::isSnap())
{
foo = QDir(getenv("SNAP_USER_COMMON"));
}
else
{
foo = QDir(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), ".."));
}
dataPath = foo.absolutePath();
adjustedBy = "Persistent data path";
@ -631,9 +640,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("ShowGlobalGameTime", true);
m_settings->registerSetting("RecordGameTime", true);
// Minecraft launch method
m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
// Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false);
@ -707,7 +713,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
// get rid of invalid meta urls
if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https")
if (!metaUrl.isValid() || (metaUrl.scheme() != "http" && metaUrl.scheme() != "https"))
m_settings->reset("MetaURLOverride");
}
@ -779,7 +785,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
if(BuildConfig.UPDATER_ENABLED)
{
qDebug() << "Initializing updater";
#ifdef Q_OS_MAC
#if defined(Q_OS_MAC) && defined(SPARKLE_ENABLED)
m_updater.reset(new MacSparkleUpdater());
#endif
qDebug() << "<> Updater started.";
@ -1189,7 +1195,17 @@ QIcon Application::getThemedIcon(const QString& name)
return QIcon::fromTheme(name);
}
bool Application::openJsonEditor(const QString &filename)
QList<CatPack*> Application::getValidCatPacks()
{
return m_themeManager->getValidCatPacks();
}
QString Application::getCatPack(QString catName)
{
return m_themeManager->getCatPack(catName);
}
bool Application::openJsonEditor(const QString& filename)
{
const QString file = QDir::current().absoluteFilePath(filename);
if (m_settings->get("JsonEditor").toString().isEmpty())

View File

@ -48,6 +48,7 @@
#include <BaseInstance.h>
#include "minecraft/launch/MinecraftServerTarget.h"
#include "ui/themes/CatPack.h"
class LaunchController;
class LocalPeer;
@ -126,9 +127,11 @@ public:
void setApplicationTheme(const QString& name);
shared_qobject_ptr<ExternalUpdater> updater() {
return m_updater;
}
QList<CatPack*> getValidCatPacks();
QString getCatPack(QString catName = "");
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
void triggerUpdateCheck();

View File

@ -262,8 +262,6 @@ set(MINECRAFT_SOURCES
minecraft/launch/CreateGameFolders.h
minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h
minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
@ -501,6 +499,11 @@ set(FTB_SOURCES
modplatform/legacy_ftb/PrivatePackManager.cpp
modplatform/legacy_ftb/PackHelpers.h
modplatform/import_ftb/PackInstallTask.h
modplatform/import_ftb/PackInstallTask.cpp
modplatform/import_ftb/PackHelpers.h
modplatform/import_ftb/PackHelpers.cpp
)
set(FLAME_SOURCES
@ -668,7 +671,7 @@ set(LOGIC_SOURCES
${ATLAUNCHER_SOURCES}
)
if(APPLE)
if(APPLE AND Launcher_ENABLE_UPDATER)
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
endif()
@ -761,6 +764,8 @@ SET(LAUNCHER_SOURCES
ui/themes/SystemTheme.h
ui/themes/ThemeManager.cpp
ui/themes/ThemeManager.h
ui/themes/CatPack.cpp
ui/themes/CatPack.h
# Processes
LaunchController.h
@ -870,6 +875,11 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/legacy_ftb/ListModel.h
ui/pages/modplatform/legacy_ftb/ListModel.cpp
ui/pages/modplatform/import_ftb/ImportFTBPage.cpp
ui/pages/modplatform/import_ftb/ImportFTBPage.h
ui/pages/modplatform/import_ftb/ListModel.h
ui/pages/modplatform/import_ftb/ListModel.cpp
ui/pages/modplatform/flame/FlameModel.cpp
ui/pages/modplatform/flame/FlameModel.h
ui/pages/modplatform/flame/FlamePage.cpp
@ -1044,6 +1054,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/ResourcePage.ui
ui/pages/modplatform/flame/FlamePage.ui
ui/pages/modplatform/legacy_ftb/Page.ui
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
@ -1139,17 +1150,23 @@ if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256})
file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle)
if(Launcher_ENABLE_UPDATER)
file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256})
file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle)
find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
add_compile_definitions(SPARKLE_ENABLED)
endif()
find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
target_link_libraries(Launcher_logic
"-framework AppKit"
"-framework Carbon"
"-framework Foundation"
"-framework ApplicationServices"
)
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
if(Launcher_ENABLE_UPDATER)
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
endif()
endif()
target_link_libraries(Launcher_logic)
@ -1211,7 +1228,7 @@ if(WIN32)
)
endif()
if (UNIX AND APPLE)
if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER)
# Add Sparkle updater
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
# the framework aren't installed

View File

@ -118,7 +118,7 @@ bool openDirectory(const QString &path, bool ensureExists)
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak())
if(!isSandbox())
{
return IndirectOpen(f);
}
@ -139,7 +139,7 @@ bool openFile(const QString &path)
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak())
if(!isSandbox())
{
return IndirectOpen(f);
}
@ -157,7 +157,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
qDebug() << "Opening file" << path << "using" << application;
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
if(!isFlatpak())
if(!isSandbox())
{
return IndirectOpen([&]()
{
@ -177,7 +177,7 @@ bool run(const QString &application, const QStringList &args, const QString &wor
{
qDebug() << "Running" << application << "with args" << args.join(' ');
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak())
if(!isSandbox())
{
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
return IndirectOpen([&]()
@ -202,7 +202,7 @@ bool openUrl(const QUrl &url)
return QDesktopServices::openUrl(url);
};
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
if(!isFlatpak())
if(!isSandbox())
{
return IndirectOpen(f);
}
@ -224,4 +224,18 @@ bool isFlatpak()
#endif
}
bool isSnap()
{
#ifdef Q_OS_LINUX
return getenv("SNAP");
#else
return false;
#endif
}
bool isSandbox()
{
return isSnap() || isFlatpak();
}
}

View File

@ -34,5 +34,18 @@ namespace DesktopServices
*/
bool openUrl(const QUrl &url);
/**
* Determine whether the launcher is running in a Flatpak environment
*/
bool isFlatpak();
/**
* Determine whether the launcher is running in a Snap environment
*/
bool isSnap();
/**
* Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment
*/
bool isSandbox();
}

View File

@ -99,7 +99,7 @@ void InstanceImportTask::executeTask()
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
@ -293,7 +293,7 @@ void InstanceImportTask::processFlame()
});
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
@ -385,7 +385,7 @@ void InstanceImportTask::processModrinth()
});
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);

View File

@ -799,7 +799,7 @@ class InstanceStaging : public Task {
connect(child, &Task::status, this, &InstanceStaging::setStatus);
connect(child, &Task::details, this, &InstanceStaging::setDetails);
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress);
connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress);
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
}

View File

@ -54,7 +54,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed);
addTask(m_filesNetJob);

View File

@ -28,7 +28,7 @@ void Update::executeTask()
{
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::propogateStepProgress);
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();

View File

@ -61,7 +61,6 @@
#include "launch/steps/QuitAfterGameStop.h"
#include "minecraft/launch/LauncherPartLaunch.h"
#include "minecraft/launch/DirectJavaLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h"
#include "minecraft/launch/ClaimAccount.h"
#include "minecraft/launch/ReconstructAssets.h"
@ -167,10 +166,6 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("MaxMemAlloc"), memorySetting);
m_settings->registerOverride(global_settings->getSetting("PermGen"), memorySetting);
// Minecraft launch method
auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
m_settings->registerOverride(global_settings->getSetting("MCLaunchMethod"), launchMethodOverride);
// Native library workarounds
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
@ -858,7 +853,7 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
{
addToFilter(sessionRef.session, tr("<SESSION ID>"));
}
if (sessionRef.access_token != "offline") {
if (sessionRef.access_token != "0") {
addToFilter(sessionRef.access_token, tr("<ACCESS TOKEN>"));
}
if(sessionRef.client_token.size()) {
@ -1005,15 +1000,6 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
process->appendStep(makeShared<CheckJava>(pptr));
}
// check launch method
QStringList validMethods = {"LauncherPart", "DirectJava"};
QString method = launchMethod();
if(!validMethods.contains(method))
{
process->appendStep(makeShared<TextPrint>(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
return process;
}
// create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732)
{
process->appendStep(makeShared<CreateGameFolders>(pptr));
@ -1087,23 +1073,11 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
{
// actually launch the game
auto method = launchMethod();
if(method == "LauncherPart")
{
auto step = makeShared<LauncherPartLaunch>(pptr);
step->setWorkingDirectory(gameRoot());
step->setAuthSession(session);
step->setServerToJoin(serverToJoin);
process->appendStep(step);
}
else if (method == "DirectJava")
{
auto step = makeShared<DirectJavaLaunch>(pptr);
step->setWorkingDirectory(gameRoot());
step->setAuthSession(session);
step->setServerToJoin(serverToJoin);
process->appendStep(step);
}
auto step = makeShared<LauncherPartLaunch>(pptr);
step->setWorkingDirectory(gameRoot());
step->setAuthSession(session);
step->setServerToJoin(serverToJoin);
process->appendStep(step);
}
// run post-exit command if that's needed
@ -1126,11 +1100,6 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
return m_launchProcess;
}
QString MinecraftInstance::launchMethod()
{
return settings()->get("MCLaunchMethod").toString();
}
JavaVersion MinecraftInstance::getJavaVersion()
{
return JavaVersion(settings()->get("JavaVersion").toString());

View File

@ -166,8 +166,6 @@ public:
protected:
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
QStringList validLaunchMethods();
QString launchMethod();
protected: // data
std::shared_ptr<PackProfile> m_components;

View File

@ -22,7 +22,7 @@ void MinecraftLoadAndCheck::executeTask()
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::stepProgress, this, &MinecraftLoadAndCheck::propogateStepProgress);
connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propagateStepProgress);
connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
}

View File

@ -100,7 +100,7 @@ void MinecraftUpdate::next()
disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
disconnect(task.get(), &Task::aborted, this, &Task::abort);
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress);
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
}
@ -120,7 +120,7 @@ void MinecraftUpdate::next()
connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
connect(task.get(), &Task::aborted, this, &Task::abort);
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress);
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
// if the task is already running, do not start it again

View File

@ -374,6 +374,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
}
yggdrasilToken = tokenFromJSONV3(data, "ygg");
// versions before 7.2 used "offline" as the offline token
if (yggdrasilToken.token == "offline")
yggdrasilToken.token = "0";
minecraftProfile = profileFromJSONV3(data, "profile");
if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
if(minecraftProfile.validity != Katabasis::Validity::None) {

View File

@ -94,7 +94,7 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
{
auto account = makeShared<MinecraftAccount>();
account->data.type = AccountType::Offline;
account->data.yggdrasilToken.token = "offline";
account->data.yggdrasilToken.token = "0";
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
account->data.yggdrasilToken.extra["userName"] = username;

View File

@ -1,166 +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 "DirectJavaLaunch.h"
#include <QStandardPaths>
#include <launch/LaunchTask.h>
#include <minecraft/MinecraftInstance.h>
#include <FileSystem.h>
#include <Commandline.h>
#include "Application.h"
#ifdef Q_OS_LINUX
#include "gamemode_client.h"
#endif
DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent)
{
connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state);
}
void DirectJavaLaunch::executeTask()
{
auto instance = m_parent->instance();
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
QStringList args = minecraftInstance->javaArguments();
args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
auto classPathEntries = minecraftInstance->getClassPath();
args.append("-cp");
QString classpath;
#ifdef Q_OS_WIN32
classpath = classPathEntries.join(';');
#else
classpath = classPathEntries.join(':');
#endif
args.append(classpath);
args.append(minecraftInstance->getMainClass());
QString allArgs = args.join(", ");
emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher);
auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
m_process.setProcessEnvironment(instance->createLaunchEnvironment());
// make detachable - this will keep the process running even if the object is destroyed
m_process.setDetachable(true);
auto mcArgs = minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin);
args.append(mcArgs);
QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
if(!wrapperCommandStr.isEmpty())
{
auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
auto wrapperCommand = wrapperArgs.takeFirst();
auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
if (realWrapperCommand.isEmpty())
{
const char *reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal);
emitFailed(tr(reason).arg(wrapperCommand));
return;
}
emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::Launcher);
args.prepend(javaPath);
m_process.start(wrapperCommand, wrapperArgs + args);
}
else
{
m_process.start(javaPath, args);
}
#ifdef Q_OS_LINUX
if (instance->settings()->get("EnableFeralGamemode").toBool() && APPLICATION->capabilities() & Application::SupportsGameMode)
{
auto pid = m_process.processId();
if (pid)
{
gamemode_request_start_for(pid);
}
}
#endif
}
void DirectJavaLaunch::on_state(LoggedProcess::State state)
{
switch(state)
{
case LoggedProcess::FailedToStart:
{
//: Error message displayed if instance can't start
const char *reason = QT_TR_NOOP("Could not launch Minecraft!");
emit logLine(reason, MessageLevel::Fatal);
emitFailed(tr(reason));
return;
}
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
{
m_parent->setPid(-1);
emitFailed(tr("Game crashed."));
return;
}
case LoggedProcess::Finished:
{
m_parent->setPid(-1);
// if the exit code wasn't 0, report this as a crash
auto exitCode = m_process.exitCode();
if(exitCode != 0)
{
emitFailed(tr("Game crashed."));
return;
}
//FIXME: make this work again
// m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
// run post-exit
emitSucceeded();
break;
}
case LoggedProcess::Running:
emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::Launcher);
m_parent->setPid(m_process.processId());
m_parent->instance()->setLastLaunch();
break;
default:
break;
}
}
void DirectJavaLaunch::setWorkingDirectory(const QString &wd)
{
m_process.setWorkingDirectory(wd);
}
void DirectJavaLaunch::proceed()
{
// nil
}
bool DirectJavaLaunch::abort()
{
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
}

View File

@ -1,58 +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 <launch/LaunchStep.h>
#include <LoggedProcess.h>
#include <minecraft/auth/AuthSession.h>
#include "MinecraftServerTarget.h"
class DirectJavaLaunch: public LaunchStep
{
Q_OBJECT
public:
explicit DirectJavaLaunch(LaunchTask *parent);
virtual ~DirectJavaLaunch() {};
virtual void executeTask();
virtual bool abort();
virtual void proceed();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
void setAuthSession(AuthSessionPtr session)
{
m_session = session;
}
void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
{
m_serverToJoin = std::move(serverToJoin);
}
private slots:
void on_state(LoggedProcess::State state);
private:
LoggedProcess m_process;
QString m_command;
AuthSessionPtr m_session;
MinecraftServerTargetPtr m_serverToJoin;
};

View File

@ -1,9 +1,9 @@
#include "LocalModParseTask.h"
#include <qdcss.h>
#include <quazip/quazip.h>
#include <quazip/quazipfile.h>
#include <toml++/toml.h>
#include <qdcss.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
@ -369,12 +369,11 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
details.icon_file = icon.toString();
}
}
}
return details;
}
ModDetails ReadForgeInfo(QString fileName)
ModDetails ReadForgeInfo(QByteArray contents)
{
ModDetails details;
// Read the data
@ -382,7 +381,7 @@ ModDetails ReadForgeInfo(QString fileName)
details.mod_id = "Forge";
details.homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini;
if (!ini.loadFile(fileName))
if (!ini.loadFile(contents))
return details;
QString major = ini.get("forge.major.number", "0").toString();
@ -554,7 +553,7 @@ bool processZIP(Mod& mod, ProcessingLevel level)
return false;
}
details = ReadForgeInfo(file.getFileName());
details = ReadForgeInfo(file.readAll());
file.close();
zip.close();

View File

@ -45,7 +45,7 @@ void AssetUpdateTask::executeTask()
connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress);
connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress);
qDebug() << m_inst->name() << ": Starting asset index download";
downloadJob->start();
@ -84,7 +84,7 @@ void AssetUpdateTask::assetIndexFinished()
connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress);
connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress);
downloadJob->start();
return;
}

View File

@ -75,7 +75,7 @@ void FMLLibrariesTask::executeTask()
connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed);
connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(dljob.get(), &NetJob::progress, this, &FMLLibrariesTask::progress);
connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propogateStepProgress);
connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propagateStepProgress);
downloadJob.reset(dljob);
downloadJob->start();
}

View File

@ -70,7 +70,7 @@ void LibrariesTask::executeTask()
connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress);
connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propogateStepProgress);
connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propagateStepProgress);
downloadJob->start();
}

View File

@ -684,7 +684,7 @@ void PackInstallTask::installConfigs()
abortable = true;
setProgress(current, total);
});
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&]{
abortable = false;
jobPtr.reset();
@ -852,7 +852,7 @@ void PackInstallTask::downloadMods()
abortable = true;
setProgress(current, total);
});
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&]
{
abortable = false;

View File

@ -52,7 +52,7 @@ void Flame::FileResolvingTask::executeTask()
stepProgress(*step_progress);
emitFailed(reason);
});
connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
qDebug() << "Resolve slug progress" << current << total;
step_progress->update(current, total);
@ -118,7 +118,7 @@ void Flame::FileResolvingTask::netJobFinished()
stepProgress(*step_progress);
emitFailed(reason);
});
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
qDebug() << "Resolve slug progress" << current << total;
step_progress->update(current, total);
@ -195,7 +195,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
stepProgress(*step_progress);
emitFailed(reason);
});
connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
qDebug() << "Resolve slug progress" << current << total;
step_progress->update(current, total);

View File

@ -57,15 +57,11 @@
#include <QDebug>
#include <QFileInfo>
#include "meta/Index.h"
#include "meta/VersionList.h"
#include "minecraft/World.h"
#include "minecraft/mod/tasks/LocalResourceParse.h"
const static QMap<QString, QString> forgemap = { { "1.2.5", "3.4.9.171" },
{ "1.4.2", "6.0.1.355" },
{ "1.4.7", "6.6.2.534" },
{ "1.5.2", "7.8.1.737" } };
static const FlameAPI api;
bool FlameCreationTask::abort()
@ -259,6 +255,56 @@ bool FlameCreationTask::updateInstance()
return false;
}
QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion)
{
if (loaderVersion == "recommended") {
auto vlist = APPLICATION->metadataIndex()->get(uid);
if (!vlist) {
setError(tr("Failed to get local metadata index for %1").arg(uid));
return {};
}
if (!vlist->isLoaded()) {
QEventLoop loadVersionLoop;
auto task = vlist->getLoadTask();
connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
if (!task->isRunning())
task->start();
loadVersionLoop.exec();
}
for (auto version : vlist->versions()) {
// first recommended build we find, we use.
if (!version->isRecommended())
continue;
auto reqs = version->requiredSet();
// filter by minecraft version, if the loader depends on a certain version.
// not all mod loaders depend on a given Minecraft version, so we won't do this
// filtering for those loaders.
if (loaderType == "forge") {
auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) {
return req.uid == "net.minecraft" && req.equalsVersion == mcVersion;
});
if (iter == reqs.end())
continue;
}
return version->descriptor();
}
setError(tr("Failed to find version for %1 loader").arg(loaderType));
return {};
}
if (loaderVersion.isEmpty()) {
emitFailed(tr("No loader version set for modpack!"));
return {};
}
return loaderVersion;
}
bool FlameCreationTask::createInstance()
{
QEventLoop loop;
@ -297,22 +343,29 @@ bool FlameCreationTask::createInstance()
}
}
QString forgeVersion;
QString fabricVersion;
// TODO: is Quilt relevant here?
QString loaderType;
QString loaderUid;
QString loaderVersion;
for (auto& loader : m_pack.minecraft.modLoaders) {
auto id = loader.id;
if (id.startsWith("forge-")) {
id.remove("forge-");
forgeVersion = id;
continue;
}
if (id.startsWith("fabric-")) {
loaderType = "forge";
loaderUid = "net.minecraftforge";
} else if (loaderType == "fabric") {
id.remove("fabric-");
fabricVersion = id;
loaderType = "fabric";
loaderUid = "net.fabricmc.fabric-loader";
} else if (loaderType == "quilt") {
id.remove("quilt-");
loaderType = "quilt";
loaderUid = "org.quiltmc.quilt-loader";
} else {
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
continue;
}
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
loaderVersion = id;
}
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
@ -329,19 +382,12 @@ bool FlameCreationTask::createInstance()
auto components = instance.getPackProfile();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", mcVersion, true);
if (!forgeVersion.isEmpty()) {
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
if (forgeVersion == "recommended") {
if (forgemap.contains(mcVersion)) {
forgeVersion = forgemap[mcVersion];
} else {
logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion));
}
}
components->setComponentVersion("net.minecraftforge", forgeVersion);
if (!loaderType.isEmpty()) {
auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion);
if (version.isEmpty())
return false;
components->setComponentVersion(loaderUid, version);
}
if (!fabricVersion.isEmpty())
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
if (m_instIcon != "default") {
instance.setIconKey(m_instIcon);
@ -386,7 +432,7 @@ bool FlameCreationTask::createInstance()
});
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress);
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus);
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propogateStepProgress);
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propagateStepProgress);
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails);
m_mod_id_resolver->start();
@ -502,11 +548,11 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
m_files_job.reset();
setError(reason);
});
connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total){
connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total) {
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
setProgress(current, total);
});
connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress);
connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress);
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
setStatus(tr("Downloading mods..."));
@ -545,7 +591,6 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
setAbortable(true);
}
void FlameCreationTask::validateZIPResouces()
{
qDebug() << "Validating whether resources stored as .zip are in the right place";
@ -569,7 +614,7 @@ void FlameCreationTask::validateZIPResouces()
return localPath;
};
auto installWorld = [this](QString worldPath){
auto installWorld = [this](QString worldPath) {
qDebug() << "Installing World from" << worldPath;
QFileInfo worldFileInfo(worldPath);
World w(worldFileInfo);
@ -586,29 +631,29 @@ void FlameCreationTask::validateZIPResouces()
QString worldPath;
switch (type) {
case PackedResourceType::Mod :
case PackedResourceType::Mod:
validatePath(fileName, targetFolder, "mods");
break;
case PackedResourceType::ResourcePack :
case PackedResourceType::ResourcePack:
validatePath(fileName, targetFolder, "resourcepacks");
break;
case PackedResourceType::TexturePack :
case PackedResourceType::TexturePack:
validatePath(fileName, targetFolder, "texturepacks");
break;
case PackedResourceType::DataPack :
case PackedResourceType::DataPack:
validatePath(fileName, targetFolder, "datapacks");
break;
case PackedResourceType::ShaderPack :
case PackedResourceType::ShaderPack:
// in theroy flame API can't do this but who knows, that *may* change ?
// better to handle it if it *does* occure in the future
validatePath(fileName, targetFolder, "shaderpacks");
break;
case PackedResourceType::WorldSave :
case PackedResourceType::WorldSave:
worldPath = validatePath(fileName, targetFolder, "saves");
installWorld(worldPath);
break;
case PackedResourceType::UNKNOWN :
default :
case PackedResourceType::UNKNOWN:
default:
qDebug() << "Can't Identify" << fileName << "at" << localPath << ", leaving it where it is.";
break;
}

View File

@ -57,10 +57,7 @@ class FlameCreationTask final : public InstanceCreationTask {
QString id,
QString version_id,
QString original_instance_id = {})
: InstanceCreationTask()
, m_parent(parent)
, m_managed_id(std::move(id))
, m_managed_version_id(std::move(version_id))
: InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id))
{
setStagingPath(staging_path);
setParentSettings(global_settings);
@ -78,6 +75,7 @@ class FlameCreationTask final : public InstanceCreationTask {
void setupDownloadJob(QEventLoop&);
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
void validateZIPResouces();
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
private:
QWidget* m_parent = nullptr;

View File

@ -26,6 +26,7 @@
#include <QMessageBox>
#include <QtConcurrentRun>
#include <algorithm>
#include <iterator>
#include <memory>
#include "Json.h"
#include "MMCZip.h"
@ -64,20 +65,11 @@ void FlamePackExportTask::executeTask()
bool FlamePackExportTask::abort()
{
if (task != nullptr) {
if (task) {
task->abort();
task = nullptr;
emitAborted();
return true;
}
if (buildZipFuture.isRunning()) {
buildZipFuture.cancel();
// NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur
// immediately.
return true;
}
return false;
}
@ -166,7 +158,7 @@ void FlamePackExportTask::collectHashes()
stepProgress(*progressStep);
emitFailed(reason);
});
connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propogateStepProgress);
connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress);
connect(hashingTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
progressStep->update(current, total);
@ -336,89 +328,40 @@ void FlamePackExportTask::buildZip()
setStatus(tr("Adding files..."));
setProgress(4, 5);
buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() {
QuaZip zip(output);
if (!zip.open(QuaZip::mdCreate)) {
QFile::remove(output);
return BuildZipResult(tr("Could not create file"));
}
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true);
zipTask->addExtraFile("manifest.json", generateIndex());
zipTask->addExtraFile("modlist.html", generateHTML());
if (buildZipFuture.isCanceled())
return BuildZipResult();
QStringList exclude;
std::transform(resolvedFiles.keyBegin(), resolvedFiles.keyEnd(), std::back_insert_iterator(exclude),
[this](QString file) { return gameRoot.relativeFilePath(file); });
zipTask->setExcludeFiles(exclude);
QuaZipFile indexFile(&zip);
if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("manifest.json"))) {
QFile::remove(output);
return BuildZipResult(tr("Could not create index"));
}
indexFile.write(generateIndex());
QuaZipFile modlist(&zip);
if (!modlist.open(QIODevice::WriteOnly, QuaZipNewInfo("modlist.html"))) {
QFile::remove(output);
return BuildZipResult(tr("Could not create index"));
}
QString content = "";
for (auto mod : resolvedFiles) {
if (mod.isMod) {
content += QString(TEMPLATE)
.replace("{name}", mod.name.toHtmlEscaped())
.replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped())
.replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : "");
}
}
content = "<ul>" + content + "</ul>";
modlist.write(content.toUtf8());
auto progressStep = std::make_shared<TaskStepProgress>();
size_t progress = 0;
for (const QFileInfo& file : files) {
if (buildZipFuture.isCanceled()) {
QFile::remove(output);
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
return BuildZipResult();
}
progressStep->update(progress, files.length());
stepProgress(*progressStep);
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
if (!resolvedFiles.contains(file.absoluteFilePath()) &&
!JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) {
QFile::remove(output);
return BuildZipResult(tr("Could not read and compress %1").arg(relative));
}
progress++;
}
zip.close();
if (zip.getZipError() != 0) {
QFile::remove(output);
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
return BuildZipResult(tr("A zip error occurred"));
}
auto progressStep = std::make_shared<TaskStepProgress>();
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
progressStep->state = TaskStepState::Succeeded;
stepProgress(*progressStep);
return BuildZipResult();
});
connect(&buildZipWatcher, &QFutureWatcher<BuildZipResult>::finished, this, &FlamePackExportTask::finish);
buildZipWatcher.setFuture(buildZipFuture);
}
void FlamePackExportTask::finish()
{
if (buildZipFuture.isCanceled())
emitAborted();
else {
const BuildZipResult result = buildZipFuture.result();
if (result.has_value())
emitFailed(result.value());
else
emitSucceeded();
}
connect(zipTask.get(), &Task::succeeded, this, &FlamePackExportTask::emitSucceeded);
connect(zipTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted);
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
emitFailed(reason);
});
connect(zipTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress);
connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
progressStep->update(current, total);
stepProgress(*progressStep);
});
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
progressStep->status = status;
stepProgress(*progressStep);
});
task.reset(zipTask);
zipTask->start();
}
QByteArray FlamePackExportTask::generateIndex()
@ -471,3 +414,18 @@ QByteArray FlamePackExportTask::generateIndex()
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
}
QByteArray FlamePackExportTask::generateHTML()
{
QString content = "";
for (auto mod : resolvedFiles) {
if (mod.isMod) {
content += QString(TEMPLATE)
.replace("{name}", mod.name.toHtmlEscaped())
.replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped())
.replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : "");
}
}
content = "<ul>" + content + "</ul>";
return content.toUtf8();
}

View File

@ -19,8 +19,6 @@
#pragma once
#include <QFuture>
#include <QFutureWatcher>
#include "BaseInstance.h"
#include "MMCZip.h"
#include "minecraft/MinecraftInstance.h"
@ -52,7 +50,6 @@ class FlamePackExportTask : public Task {
const QString output;
const MMCZip::FilterFunction filter;
typedef std::optional<QString> BuildZipResult;
struct ResolvedFile {
int addonId;
int version;
@ -76,15 +73,13 @@ class FlamePackExportTask : public Task {
QMap<QString, HashInfo> pendingHashes{};
QMap<QString, ResolvedFile> resolvedFiles{};
Task::Ptr task;
QFuture<BuildZipResult> buildZipFuture;
QFutureWatcher<BuildZipResult> buildZipWatcher;
void collectFiles();
void collectHashes();
void makeApiRequest();
void getProjectsInfo();
void buildZip();
void finish();
QByteArray generateIndex();
QByteArray generateHTML();
};

View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 "modplatform/import_ftb/PackHelpers.h"
#include <QIcon>
#include <QString>
#include <QVariant>
#include "FileSystem.h"
#include "Json.h"
namespace FTBImportAPP {
Modpack parseDirectory(QString path)
{
Modpack modpack{ path };
auto instanceFile = QFileInfo(FS::PathCombine(path, "instance.json"));
if (!instanceFile.exists() || !instanceFile.isFile())
return {};
try {
auto doc = Json::requireDocument(instanceFile.absoluteFilePath(), "FTB_APP instance JSON file");
const auto root = doc.object();
modpack.uuid = Json::requireString(root, "uuid", "uuid");
modpack.id = Json::requireInteger(root, "id", "id");
modpack.versionId = Json::requireInteger(root, "versionId", "versionId");
modpack.name = Json::requireString(root, "name", "name");
modpack.version = Json::requireString(root, "version", "version");
modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
} catch (const Exception& e) {
qDebug() << "Couldn't load ftb instance json: " << e.cause();
return {};
}
auto versionsFile = QFileInfo(FS::PathCombine(path, "version.json"));
if (!versionsFile.exists() || !versionsFile.isFile())
return {};
try {
auto doc = Json::requireDocument(versionsFile.absoluteFilePath(), "FTB_APP version JSON file");
const auto root = doc.object();
auto targets = Json::requireArray(root, "targets", "targets");
for (auto target : targets) {
auto obj = Json::requireObject(target, "target");
auto name = Json::requireString(obj, "name", "name");
auto version = Json::requireString(obj, "version", "version");
if (name == "forge") {
modpack.loaderType = ResourceAPI::Forge;
modpack.version = version;
break;
} else if (name == "fabric") {
modpack.loaderType = ResourceAPI::Fabric;
modpack.version = version;
break;
} else if (name == "quilt") {
modpack.loaderType = ResourceAPI::Quilt;
modpack.version = version;
break;
}
}
} catch (const Exception& e) {
qDebug() << "Couldn't load ftb version json: " << e.cause();
return {};
}
auto iconFile = QFileInfo(FS::PathCombine(path, "folder.jpg"));
if (iconFile.exists() && iconFile.isFile()) {
modpack.icon = QIcon(iconFile.absoluteFilePath());
}
return modpack;
}
} // namespace FTBImportAPP

View File

@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <QIcon>
#include <QList>
#include <QMetaType>
#include <QString>
#include <QVariant>
#include "modplatform/ResourceAPI.h"
namespace FTBImportAPP {
struct Modpack {
QString path;
// json data
QString uuid;
int id;
int versionId;
QString name;
QString version;
QString mcVersion;
// not needed for instance creation
QVariant jvmArgs;
std::optional<ResourceAPI::ModLoaderType> loaderType;
QString loaderVersion;
QIcon icon;
};
typedef QList<Modpack> ModpackList;
Modpack parseDirectory(QString path);
} // namespace FTBImportAPP
// We need it for the proxy model
Q_DECLARE_METATYPE(FTBImportAPP::Modpack)

View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 "PackInstallTask.h"
#include <QtConcurrent>
#include "BaseInstance.h"
#include "FileSystem.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "modplatform/ResourceAPI.h"
#include "modplatform/import_ftb/PackHelpers.h"
#include "settings/INISettingsObject.h"
namespace FTBImportAPP {
void PackInstallTask::executeTask()
{
setStatus(tr("Copying files..."));
setAbortable(false);
progress(1, 2);
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, ".minecraft"));
folderCopy.followSymlinks(true);
return folderCopy();
});
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &PackInstallTask::copySettings);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &PackInstallTask::emitAborted);
m_copyFutureWatcher.setFuture(m_copyFuture);
}
void PackInstallTask::copySettings()
{
setStatus(tr("Copying settings..."));
progress(2, 2);
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
instanceSettings->suspendSave();
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
instance.settings()->set("InstanceType", "OneSix");
if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) {
instance.settings()->set("OverrideJavaArgs", true);
instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString());
}
auto components = instance.getPackProfile();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
auto modloader = m_pack.loaderType;
if (modloader.has_value())
switch (modloader.value()) {
case ResourceAPI::Forge: {
components->setComponentVersion("net.minecraftforge", m_pack.version, true);
break;
}
case ResourceAPI::Fabric: {
components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true);
break;
}
case ResourceAPI::Quilt: {
components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true);
break;
}
case ResourceAPI::Cauldron:
break;
case ResourceAPI::LiteLoader:
break;
}
components->saveNow();
instance.setName(name());
if (m_instIcon == "default")
m_instIcon = "ftb_logo";
instance.setIconKey(m_instIcon);
instanceSettings->resumeSave();
emitSucceeded();
}
} // namespace FTBImportAPP

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <QFuture>
#include <QFutureWatcher>
#include "InstanceTask.h"
#include "PackHelpers.h"
namespace FTBImportAPP {
class PackInstallTask : public InstanceTask {
Q_OBJECT
public:
explicit PackInstallTask(const Modpack& pack) : m_pack(pack) {}
virtual ~PackInstallTask() = default;
protected:
virtual void executeTask() override;
private slots:
void copySettings();
private:
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
const Modpack m_pack;
};
} // namespace FTBImportAPP

View File

@ -81,7 +81,7 @@ void PackInstallTask::downloadPack()
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted);
netJobContainer->start();

View File

@ -267,7 +267,7 @@ bool ModrinthCreationTask::createInstance()
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
setProgress(current, total);
});
connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress);
connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress);
setStatus(tr("Downloading mods..."));
m_files_job->start();

View File

@ -55,20 +55,11 @@ void ModrinthPackExportTask::executeTask()
bool ModrinthPackExportTask::abort()
{
if (task != nullptr) {
if (task) {
task->abort();
task = nullptr;
emitAborted();
return true;
}
if (buildZipFuture.isRunning()) {
buildZipFuture.cancel();
// NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur
// immediately.
return true;
}
return false;
}
@ -205,63 +196,36 @@ void ModrinthPackExportTask::buildZip()
{
setStatus(tr("Adding files..."));
buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() {
QuaZip zip(output);
if (!zip.open(QuaZip::mdCreate)) {
QFile::remove(output);
return BuildZipResult(tr("Could not create file"));
}
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true);
zipTask->addExtraFile("modrinth.index.json", generateIndex());
if (buildZipFuture.isCanceled())
return BuildZipResult();
zipTask->setExcludeFiles(resolvedFiles.keys());
QuaZipFile indexFile(&zip);
if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("modrinth.index.json"))) {
QFile::remove(output);
return BuildZipResult(tr("Could not create index"));
}
indexFile.write(generateIndex());
size_t progress = 0;
for (const QFileInfo& file : files) {
if (buildZipFuture.isCanceled()) {
QFile::remove(output);
return BuildZipResult();
}
setProgress(progress, files.length());
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
if (!resolvedFiles.contains(relative) && !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) {
QFile::remove(output);
return BuildZipResult(tr("Could not read and compress %1").arg(relative));
}
progress++;
}
zip.close();
if (zip.getZipError() != 0) {
QFile::remove(output);
return BuildZipResult(tr("A zip error occurred"));
}
return BuildZipResult();
auto progressStep = std::make_shared<TaskStepProgress>();
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
progressStep->state = TaskStepState::Succeeded;
stepProgress(*progressStep);
});
connect(&buildZipWatcher, &QFutureWatcher<BuildZipResult>::finished, this, &ModrinthPackExportTask::finish);
buildZipWatcher.setFuture(buildZipFuture);
}
void ModrinthPackExportTask::finish()
{
if (buildZipFuture.isCanceled())
emitAborted();
else {
const BuildZipResult result = buildZipFuture.result();
if (result.has_value())
emitFailed(result.value());
else
emitSucceeded();
}
connect(zipTask.get(), &Task::succeeded, this, &ModrinthPackExportTask::emitSucceeded);
connect(zipTask.get(), &Task::aborted, this, &ModrinthPackExportTask::emitAborted);
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
emitFailed(reason);
});
connect(zipTask.get(), &Task::stepProgress, this, &ModrinthPackExportTask::propagateStepProgress);
connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
progressStep->update(current, total);
stepProgress(*progressStep);
});
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
progressStep->status = status;
stepProgress(*progressStep);
});
task.reset(zipTask);
zipTask->start();
}
QByteArray ModrinthPackExportTask::generateIndex()

View File

@ -56,22 +56,17 @@ class ModrinthPackExportTask : public Task {
const QString output;
const MMCZip::FilterFunction filter;
typedef std::optional<QString> BuildZipResult;
ModrinthAPI api;
QFileInfoList files;
QMap<QString, QString> pendingHashes;
QMap<QString, ResolvedFile> resolvedFiles;
Task::Ptr task;
QFuture<BuildZipResult> buildZipFuture;
QFutureWatcher<BuildZipResult> buildZipWatcher;
void collectFiles();
void collectHashes();
void makeApiRequest();
void parseApiResponse(const std::shared_ptr<QByteArray> response);
void buildZip();
void finish();
QByteArray generateIndex();
};

View File

@ -50,7 +50,7 @@ void Technic::SingleZipPackInstallTask::executeTask()
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged);
connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propogateStepProgress);
connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propagateStepProgress);
connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed);
m_filesNetJob->start();
}

View File

@ -126,7 +126,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propagateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted);
m_filesNetJob->start();

View File

@ -37,11 +37,12 @@
#include "settings/INIFile.h"
#include <FileSystem.h>
#include <QFile>
#include <QTextStream>
#include <QStringList>
#include <QSaveFile>
#include <QDebug>
#include <QFile>
#include <QSaveFile>
#include <QStringList>
#include <QTemporaryFile>
#include <QTextStream>
#include <QSettings>
@ -71,6 +72,7 @@ bool INIFile::saveFile(QString fileName)
return true;
}
QString unescape(QString orig)
{
QString out;
@ -185,6 +187,19 @@ bool INIFile::loadFile(QString fileName)
return true;
}
bool INIFile::loadFile(QByteArray data)
{
QTemporaryFile file;
if (!file.open())
return false;
file.write(data);
file.flush();
file.close();
auto loaded = loadFile(file.fileName());
file.remove();
return loaded;
}
QVariant INIFile::get(QString key, QVariant def) const
{
if (!this->contains(key))

View File

@ -50,6 +50,7 @@ public:
explicit INIFile();
bool loadFile(QString fileName);
bool loadFile(QByteArray data);
bool saveFile(QString fileName);
QVariant get(QString key, QVariant def) const;

View File

@ -161,7 +161,7 @@ void Task::emitSucceeded()
emit finished();
}
void Task::propogateStepProgress(TaskStepProgress const& task_progress)
void Task::propagateStepProgress(TaskStepProgress const& task_progress)
{
emit stepProgress(task_progress);
}

View File

@ -167,7 +167,7 @@ class Task : public QObject, public QRunnable {
virtual void emitAborted();
virtual void emitFailed(QString reason = "");
virtual void propogateStepProgress(TaskStepProgress const& task_progress);
virtual void propagateStepProgress(TaskStepProgress const& task_progress);
public slots:
void setStatus(const QString& status);

View File

@ -1342,16 +1342,10 @@ void MainWindow::on_actionExportInstanceFlamePack_triggered()
if (m_selectedInstance) {
auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
if (instance) {
QString errorMsg;
if (instance->getPackProfile()->getComponent("org.quiltmc.quilt-loader")) {
errorMsg = tr("Quilt is currently not supported by CurseForge modpacks.");
} else if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft");
cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") {
errorMsg = tr("Snapshots are currently not supported by CurseForge modpacks.");
}
if (!errorMsg.isEmpty()) {
QMessageBox msgBox;
msgBox.setText(errorMsg);
if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft");
cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") {
QMessageBox msgBox(this);
msgBox.setText("Snapshots are currently not supported by CurseForge modpacks.");
msgBox.exec();
return;
}

View File

@ -170,7 +170,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>");
ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT));
QString copyText("© 2022 %1");
QString copyText("© 2022-2023 %1");
ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT));
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));

View File

@ -70,6 +70,8 @@ ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget* parent
auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot());
proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") });
proxyModel->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
proxyModel->ignoreFilesWithPath().insert(
{ FS::PathCombine(prefix, ".cache"), FS::PathCombine(prefix, ".fabric"), FS::PathCombine(prefix, ".quilt") });
loadPackIgnore();
ui->treeView->setModel(proxyModel);

View File

@ -61,7 +61,7 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla
// use the game root - everything outside cannot be exported
const QDir root(instance->gameRoot());
proxy = new FileIgnoreProxy(instance->gameRoot(), this);
proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports" });
proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports", ".cache", ".fabric", ".quilt" });
proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
proxy->setSourceModel(model);

View File

@ -33,38 +33,37 @@
* limitations under the License.
*/
#include "Application.h"
#include "NewInstanceDialog.h"
#include "Application.h"
#include "ui/pages/modplatform/import_ftb/ImportFTBPage.h"
#include "ui_NewInstanceDialog.h"
#include <BaseVersion.h>
#include <InstanceList.h>
#include <icons/IconList.h>
#include <tasks/Task.h>
#include <InstanceList.h>
#include "VersionSelectDialog.h"
#include "ProgressDialog.h"
#include "IconPickerDialog.h"
#include "ProgressDialog.h"
#include "VersionSelectDialog.h"
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QLayout>
#include <QPushButton>
#include <QFileDialog>
#include <QValidator>
#include <QDialogButtonBox>
#include <utility>
#include "ui/widgets/PageContainer.h"
#include "ui/pages/modplatform/CustomPage.h"
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
#include "ui/pages/modplatform/legacy_ftb/Page.h"
#include "ui/pages/modplatform/flame/FlamePage.h"
#include "ui/pages/modplatform/ImportPage.h"
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
#include "ui/pages/modplatform/flame/FlamePage.h"
#include "ui/pages/modplatform/legacy_ftb/Page.h"
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
#include "ui/pages/modplatform/technic/TechnicPage.h"
#include "ui/widgets/PageContainer.h"
NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString & url, QWidget *parent)
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& url, QWidget* parent)
: QDialog(parent), ui(new Ui::NewInstanceDialog)
{
ui->setupUi(this);
@ -168,6 +167,7 @@ QList<BasePage *> NewInstanceDialog::getPages()
if (APPLICATION->capabilities() & Application::SupportsFlame)
pages.append(new FlamePage(this));
pages.append(new LegacyFTB::Page(this));
pages.append(new FTBImportAPP::ImportFTBPage(this));
pages.append(new ModrinthPage(this));
pages.append(new TechnicPage(this));

View File

@ -48,7 +48,6 @@
#include <QAccessible>
#include "VisualGroup.h"
#include "ui/themes/ThemeManager.h"
#include <QDebug>
#include <Application.h>
@ -504,7 +503,7 @@ void InstanceView::setPaintCat(bool visible)
{
m_catVisible = visible;
if (visible)
m_catPixmap.load(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage()));
m_catPixmap.load(APPLICATION->getCatPack());
else
m_catPixmap = QPixmap();
}

View File

@ -30,7 +30,7 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string notr="true">Services</string>
<string>Services</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>

View File

@ -58,7 +58,7 @@
<item row="2" column="0">
<widget class="QLabel" name="labelPermGen">
<property name="text">
<string notr="true">&amp;PermGen:</string>
<string>&amp;PermGen:</string>
</property>
<property name="buddy">
<cstring>permGenSpinBox</cstring>

View File

@ -62,7 +62,7 @@ public:
QString displayName() const override
{
return "Launcher";
return tr("Launcher");
}
QIcon icon() const override
{

View File

@ -39,7 +39,7 @@
</property>
<widget class="QWidget" name="minecraftTab">
<attribute name="title">
<string notr="true">General</string>
<string>General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>

View File

@ -116,7 +116,7 @@
<item row="2" column="0">
<widget class="QLabel" name="labelPermGen">
<property name="text">
<string notr="true">PermGen:</string>
<string>PermGen:</string>
</property>
</widget>
</item>

View File

@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 "ImportFTBPage.h"
#include "ui_ImportFTBPage.h"
#include <QWidget>
#include "FileSystem.h"
#include "ListModel.h"
#include "modplatform/import_ftb/PackInstallTask.h"
#include "ui/dialogs/NewInstanceDialog.h"
namespace FTBImportAPP {
ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog(dialog), ui(new Ui::ImportFTBPage)
{
ui->setupUi(this);
{
listModel = new ListModel(this);
ui->modpackList->setModel(listModel);
ui->modpackList->setSortingEnabled(true);
ui->modpackList->header()->hide();
ui->modpackList->setIndentation(0);
ui->modpackList->setIconSize(QSize(42, 42));
}
connect(ui->modpackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &ImportFTBPage::onPublicPackSelectionChanged);
ui->modpackList->selectionModel()->reset();
}
ImportFTBPage::~ImportFTBPage()
{
delete ui;
}
void ImportFTBPage::openedImpl()
{
if (!initialized) {
listModel->update();
initialized = true;
}
suggestCurrent();
}
void ImportFTBPage::retranslate()
{
ui->retranslateUi(this);
}
void ImportFTBPage::suggestCurrent()
{
if (!isOpened)
return;
if (selected.path.isEmpty()) {
dialog->setSuggestedPack();
return;
}
dialog->setSuggestedPack(selected.name, new PackInstallTask(selected));
QString editedLogoName = QString("ftb_%1").arg(selected.id);
dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName);
}
void ImportFTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if (!now.isValid()) {
onPackSelectionChanged();
return;
}
Modpack selectedPack = listModel->data(now, Qt::UserRole).value<Modpack>();
onPackSelectionChanged(&selectedPack);
}
void ImportFTBPage::onPackSelectionChanged(Modpack* pack)
{
if (pack) {
selected = *pack;
suggestCurrent();
return;
}
if (isOpened)
dialog->setSuggestedPack();
}
} // namespace FTBImportAPP

View File

@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <QDialog>
#include <QTextBrowser>
#include <QTreeView>
#include <QWidget>
#include <Application.h>
#include "modplatform/import_ftb/PackHelpers.h"
#include "ui/pages/BasePage.h"
#include "ui/pages/modplatform/import_ftb/ListModel.h"
class NewInstanceDialog;
namespace FTBImportAPP {
namespace Ui {
class ImportFTBPage;
}
class ImportFTBPage : public QWidget, public BasePage {
Q_OBJECT
public:
explicit ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent = 0);
virtual ~ImportFTBPage();
QString displayName() const override { return tr("FTB App Import"); }
QIcon icon() const override { return APPLICATION->getThemedIcon("ftb_logo"); }
QString id() const override { return "import_ftb"; }
QString helpPage() const override { return "FTB-platform"; }
bool shouldDisplay() const override { return true; }
void openedImpl() override;
void retranslate() override;
private:
void suggestCurrent();
void onPackSelectionChanged(Modpack* pack = nullptr);
private slots:
void onPublicPackSelectionChanged(QModelIndex first, QModelIndex second);
private:
bool initialized = false;
Modpack selected;
ListModel* listModel = nullptr;
NewInstanceDialog* dialog = nullptr;
Ui::ImportFTBPage* ui = nullptr;
};
} // namespace FTBImportAPP

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FTBImportAPP::ImportFTBPage</class>
<widget class="QWidget" name="FTBImportAPP::ImportFTBPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1461</width>
<height>1011</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="modpackList">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 "ListModel.h"
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QIcon>
#include <QProcessEnvironment>
#include "FileSystem.h"
#include "modplatform/import_ftb/PackHelpers.h"
namespace FTBImportAPP {
QString getPath()
{
QString partialPath;
#if defined(Q_OS_OSX)
partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support");
#elif defined(Q_OS_WIN32)
partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", "");
#else
partialPath = QDir::homePath();
#endif
return FS::PathCombine(partialPath, ".ftba");
}
const QString ListModel::FTB_APP_PATH = getPath();
void ListModel::update()
{
beginResetModel();
modpacks.clear();
QString instancesPath = FS::PathCombine(FTB_APP_PATH, "instances");
if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) {
QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden,
QDirIterator::FollowSymlinks);
while (directoryIterator.hasNext()) {
auto modpack = parseDirectory(directoryIterator.next());
if (!modpack.path.isEmpty())
modpacks.append(modpack);
}
} else {
qDebug() << "Couldn't find ftb instances folder: " << instancesPath;
}
endResetModel();
}
QVariant ListModel::data(const QModelIndex& index, int role) const
{
int pos = index.row();
if (pos >= modpacks.size() || pos < 0 || !index.isValid()) {
return QVariant();
}
auto pack = modpacks.at(pos);
if (role == Qt::DisplayRole) {
return pack.name;
} else if (role == Qt::DecorationRole) {
return pack.icon;
} else if (role == Qt::UserRole) {
QVariant v;
v.setValue(pack);
return v;
} else if (role == Qt::ToolTipRole) {
return tr("Minecraft %1").arg(pack.mcVersion);
}
return QVariant();
}
} // namespace FTBImportAPP

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <QAbstractListModel>
#include <QIcon>
#include <QVariant>
#include "modplatform/import_ftb/PackHelpers.h"
namespace FTBImportAPP {
class ListModel : public QAbstractListModel {
Q_OBJECT
public:
ListModel(QObject* parent) : QAbstractListModel(parent) {}
virtual ~ListModel() = default;
int rowCount(const QModelIndex& parent) const { return modpacks.size(); }
int columnCount(const QModelIndex& parent) const { return 1; }
QVariant data(const QModelIndex& index, int role) const;
void update();
static const QString FTB_APP_PATH;
private:
ModpackList modpacks;
};
} // namespace FTBImportAPP

View File

@ -61,7 +61,7 @@ void ThemeWizardPage::updateIcons()
void ThemeWizardPage::updateCat()
{
qDebug() << "Setting Cat";
ui->catImagePreviewButton->setIcon(QIcon(QString(R"(:/backgrounds/%1)").arg(ThemeManager::getCatImage())));
ui->catImagePreviewButton->setIcon(QIcon(QString(R"(%1)").arg(APPLICATION->getCatPack())));
}
void ThemeWizardPage::retranslate()

View File

@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* 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 "ui/themes/CatPack.h"
#include <QDate>
#include <QDir>
#include <QFileInfo>
#include "FileSystem.h"
#include "Json.h"
QString BasicCatPack::path()
{
const auto now = QDate::currentDate();
const auto birthday = QDate(now.year(), 11, 30);
const auto xmas = QDate(now.year(), 12, 25);
const auto halloween = QDate(now.year(), 10, 31);
QString cat = QString(":/backgrounds/%1").arg(m_id);
if (std::abs(now.daysTo(xmas)) <= 4) {
cat += "-xmas";
} else if (std::abs(now.daysTo(halloween)) <= 4) {
cat += "-spooky";
} else if (std::abs(now.daysTo(birthday)) <= 12) {
cat += "-bday";
}
return cat;
}
JsonCatPack::PartialDate partialDate(QJsonObject date)
{
auto month = Json::ensureInteger(date, "month", 1);
if (month > 12)
month = 12;
else if (month <= 0)
month = 1;
auto day = Json::ensureInteger(date, "day", 1);
if (day > 31)
day = 31;
else if (day <= 0)
day = 1;
return { month, day };
};
JsonCatPack::JsonCatPack(QFileInfo& manifestInfo) : BasicCatPack(manifestInfo.dir().dirName())
{
QString path = manifestInfo.path();
auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "CatPack JSON file");
const auto root = doc.object();
m_name = Json::requireString(root, "name", "Catpack name");
m_defaultPath = FS::PathCombine(path, Json::requireString(root, "default", "Default Cat"));
auto variants = Json::ensureArray(root, "variants", QJsonArray(), "Catpack Variants");
for (auto v : variants) {
auto variant = Json::ensureObject(v, QJsonObject(), "Cat variant");
m_variants << Variant{ FS::PathCombine(path, Json::requireString(variant, "path", "Variant path")),
partialDate(Json::requireObject(variant, "startTime", "Variant startTime")),
partialDate(Json::requireObject(variant, "endTime", "Variant endTime")) };
}
}
QDate ensureDay(int year, int month, int day)
{
QDate date(year, month, 1);
if (day > date.daysInMonth())
day = date.daysInMonth();
return QDate(year, month, day);
}
QString JsonCatPack::path()
{
const QDate now = QDate::currentDate();
for (auto var : m_variants) {
QDate startDate = ensureDay(now.year(), var.startTime.month, var.startTime.day);
QDate endDate = ensureDay(now.year(), var.endTime.month, var.endTime.day);
if (startDate > endDate) { // it's spans over multiple years
if (endDate <= now) // end date is in the past so jump one year into the future for endDate
endDate = endDate.addYears(1);
else // end date is in the future so jump one year into the past for startDate
startDate = startDate.addYears(-1);
}
if (startDate >= now && now >= endDate)
return var.path;
}
return m_defaultPath;
}

View File

@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* 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 <QDate>
#include <QFileInfo>
#include <QList>
#include <QString>
class CatPack {
public:
virtual ~CatPack() {}
virtual QString id() = 0;
virtual QString name() = 0;
virtual QString path() = 0;
};
class BasicCatPack : public CatPack {
public:
BasicCatPack(QString id, QString name) : m_id(id), m_name(name) {}
BasicCatPack(QString id) : BasicCatPack(id, id) {}
virtual QString id() { return m_id; };
virtual QString name() { return m_name; };
virtual QString path();
protected:
QString m_id;
QString m_name;
};
class FileCatPack : public BasicCatPack {
public:
FileCatPack(QString id, QFileInfo& fileInfo) : BasicCatPack(id), m_path(fileInfo.absoluteFilePath()) {}
FileCatPack(QFileInfo& fileInfo) : FileCatPack(fileInfo.baseName(), fileInfo) {}
virtual QString path() { return m_path; }
private:
QString m_path;
};
class JsonCatPack : public BasicCatPack {
public:
struct PartialDate {
int month;
int day;
};
struct Variant {
QString path;
PartialDate startTime;
PartialDate endTime;
};
JsonCatPack(QFileInfo& manifestInfo);
virtual QString path();
private:
QString m_defaultPath;
QList<Variant> m_variants;
};

View File

@ -21,7 +21,10 @@
#include <QDir>
#include <QDirIterator>
#include <QIcon>
#include <QImageReader>
#include "Exception.h"
#include "ui/themes/BrightTheme.h"
#include "ui/themes/CatPack.h"
#include "ui/themes/CustomTheme.h"
#include "ui/themes/DarkTheme.h"
#include "ui/themes/SystemTheme.h"
@ -32,6 +35,7 @@ ThemeManager::ThemeManager(MainWindow* mainWindow)
{
m_mainWindow = mainWindow;
initializeThemes();
initializeCatPacks();
}
/// @brief Adds the Theme to the list of themes
@ -40,7 +44,10 @@ ThemeManager::ThemeManager(MainWindow* mainWindow)
QString ThemeManager::addTheme(std::unique_ptr<ITheme> theme)
{
QString id = theme->id();
m_themes.emplace(id, std::move(theme));
if (m_themes.find(id) == m_themes.end())
m_themes.emplace(id, std::move(theme));
else
themeWarningLog() << "Theme(" << id << ") not added to prevent id duplication";
return id;
}
@ -77,7 +84,7 @@ void ThemeManager::initializeThemes()
QString themeFolder = QDir("./themes/").absoluteFilePath("");
themeDebugLog() << "Theme Folder Path: " << themeFolder;
QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot);
while (directoryIterator.hasNext()) {
QDir dir(directoryIterator.next());
QFileInfo themeJson(dir.absoluteFilePath("theme.json"));
@ -111,6 +118,16 @@ QList<ITheme*> ThemeManager::getValidApplicationThemes()
return ret;
}
QList<CatPack*> ThemeManager::getValidCatPacks()
{
QList<CatPack*> ret;
ret.reserve(m_catPacks.size());
for (auto&& [id, theme] : m_catPacks) {
ret.append(theme.get());
}
return ret;
}
void ThemeManager::setIconTheme(const QString& name)
{
QIcon::setThemeName(name);
@ -137,19 +154,74 @@ void ThemeManager::setApplicationTheme(const QString& name, bool initial)
}
}
QString ThemeManager::getCatImage(QString catName)
QString ThemeManager::getCatPack(QString catName)
{
QDateTime now = QDateTime::currentDateTime();
QDateTime birthday(QDate(now.date().year(), 11, 30), QTime(0, 0));
QDateTime xmas(QDate(now.date().year(), 12, 25), QTime(0, 0));
QDateTime halloween(QDate(now.date().year(), 10, 31), QTime(0, 0));
QString cat = !catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString();
if (std::abs(now.daysTo(xmas)) <= 4) {
cat += "-xmas";
} else if (std::abs(now.daysTo(halloween)) <= 4) {
cat += "-spooky";
} else if (std::abs(now.daysTo(birthday)) <= 12) {
cat += "-bday";
auto catIter = m_catPacks.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString());
if (catIter != m_catPacks.end()) {
auto& catPack = catIter->second;
themeDebugLog() << "applying catpack" << catPack->id();
return catPack->path();
} else {
themeWarningLog() << "Tried to get invalid catPack:" << catName;
}
return m_catPacks.begin()->second->path();
}
QString ThemeManager::addCatPack(std::unique_ptr<CatPack> catPack)
{
QString id = catPack->id();
if (m_catPacks.find(id) == m_catPacks.end())
m_catPacks.emplace(id, std::move(catPack));
else
themeWarningLog() << "CatPack(" << id << ") not added to prevent id duplication";
return id;
}
void ThemeManager::initializeCatPacks()
{
QList<std::pair<QString, QString>> defaultCats{ { "kitteh", QObject::tr("Background Cat (from MultiMC)") },
{ "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") },
{ "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") },
{ "teawie", QObject::tr("Teawie (drawn by SympathyTea)") } };
for (auto [id, name] : defaultCats) {
addCatPack(std::unique_ptr<CatPack>(new BasicCatPack(id, name)));
}
QDir catpacksDir("catpacks");
QString catpacksFolder = catpacksDir.absoluteFilePath("");
themeDebugLog() << "CatPacks Folder Path:" << catpacksFolder;
QStringList supportedImageFormats;
for (auto format : QImageReader::supportedImageFormats()) {
supportedImageFormats.append("*." + format);
}
auto loadFiles = [this, supportedImageFormats](QDir dir) {
// Load image files directly
QDirIterator ImageFileIterator(dir.absoluteFilePath(""), supportedImageFormats, QDir::Files);
while (ImageFileIterator.hasNext()) {
QFile customCatFile(ImageFileIterator.next());
QFileInfo customCatFileInfo(customCatFile);
themeDebugLog() << "Loading CatPack from:" << customCatFileInfo.absoluteFilePath();
addCatPack(std::unique_ptr<CatPack>(new FileCatPack(customCatFileInfo)));
}
};
loadFiles(catpacksDir);
QDirIterator directoryIterator(catpacksFolder, QDir::Dirs | QDir::NoDotAndDotDot);
while (directoryIterator.hasNext()) {
QDir dir(directoryIterator.next());
QFileInfo manifest(dir.absoluteFilePath("catpack.json"));
if (manifest.isFile()) {
try {
// Load background manifest
themeDebugLog() << "Loading background manifest from:" << manifest.absoluteFilePath();
addCatPack(std::unique_ptr<CatPack>(new JsonCatPack(manifest)));
} catch (const Exception& e) {
themeWarningLog() << "Couldn't load catpack json:" << e.cause();
}
} else {
loadFiles(dir);
}
}
return cat;
}

View File

@ -20,6 +20,7 @@
#include <QString>
#include "ui/MainWindow.h"
#include "ui/themes/CatPack.h"
#include "ui/themes/ITheme.h"
inline auto themeDebugLog()
@ -40,18 +41,20 @@ class ThemeManager {
void applyCurrentlySelectedTheme(bool initial = false);
void setApplicationTheme(const QString& name, bool initial = false);
/// <summary>
/// Returns the cat based on selected cat and with events (Birthday, XMas, etc.)
/// </summary>
/// <param name="catName">Optional, if you need a specific cat.</param>
/// <returns></returns>
static QString getCatImage(QString catName = "");
/// @brief Returns the background based on selected and with events (Birthday, XMas, etc.)
/// @param catName Optional, if you need a specific background.
/// @return
QString getCatPack(QString catName = "");
QList<CatPack*> getValidCatPacks();
private:
std::map<QString, std::unique_ptr<ITheme>> m_themes;
std::map<QString, std::unique_ptr<CatPack>> m_catPacks;
MainWindow* m_mainWindow;
void initializeThemes();
void initializeCatPacks();
QString addTheme(std::unique_ptr<ITheme> theme);
ITheme* getTheme(QString themeId);
QString addCatPack(std::unique_ptr<CatPack> catPack);
};

View File

@ -95,9 +95,14 @@ void ThemeCustomizationWidget::applyWidgetTheme(int index) {
emit currentWidgetThemeChanged(index);
}
void ThemeCustomizationWidget::applyCatTheme(int index) {
void ThemeCustomizationWidget::applyCatTheme(int index)
{
auto settings = APPLICATION->settings();
settings->set("BackgroundCat", m_catOptions[index].first);
auto originalCat = settings->get("BackgroundCat").toString();
auto newCat = ui->backgroundCatComboBox->currentData().toString();
if (originalCat != newCat) {
settings->set("BackgroundCat", newCat);
}
emit currentCatChanged(index);
}
@ -135,10 +140,10 @@ void ThemeCustomizationWidget::loadSettings()
}
auto cat = settings->get("BackgroundCat").toString();
for (auto& catFromList : m_catOptions) {
QIcon catIcon = QIcon(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage(catFromList.first)));
ui->backgroundCatComboBox->addItem(catIcon, catFromList.second);
if (cat == catFromList.first) {
for (auto& catFromList : APPLICATION->getValidCatPacks()) {
QIcon catIcon = QIcon(QString("%1").arg(catFromList->path()));
ui->backgroundCatComboBox->addItem(catIcon, catFromList->name(), catFromList->id());
if (cat == catFromList->id()) {
ui->backgroundCatComboBox->setCurrentIndex(ui->backgroundCatComboBox->count() - 1);
}
}

View File

@ -53,25 +53,17 @@ class ThemeCustomizationWidget : public QWidget {
private:
Ui::ThemeCustomizationWidget* ui;
//TODO finish implementing
QList<std::pair<QString, QString>> m_iconThemeOptions{
{ "pe_colored", QObject::tr("Simple (Colored Icons)") },
{ "pe_light", QObject::tr("Simple (Light Icons)") },
{ "pe_dark", QObject::tr("Simple (Dark Icons)") },
{ "pe_blue", QObject::tr("Simple (Blue Icons)") },
{ "breeze_light", QObject::tr("Breeze Light") },
{ "breeze_dark", QObject::tr("Breeze Dark") },
{ "OSX", QObject::tr("OSX") },
{ "iOS", QObject::tr("iOS") },
{ "flat", QObject::tr("Flat") },
{ "flat_white", QObject::tr("Flat (White)") },
{ "multimc", QObject::tr("Legacy") },
{ "custom", QObject::tr("Custom") }
};
QList<std::pair<QString, QString>> m_catOptions{
{ "kitteh", QObject::tr("Background Cat (from MultiMC)") },
{ "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") },
{ "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") },
{ "teawie", QObject::tr("Teawie (drawn by SympathyTea)") }
};
// TODO finish implementing
QList<std::pair<QString, QString>> m_iconThemeOptions{ { "pe_colored", QObject::tr("Simple (Colored Icons)") },
{ "pe_light", QObject::tr("Simple (Light Icons)") },
{ "pe_dark", QObject::tr("Simple (Dark Icons)") },
{ "pe_blue", QObject::tr("Simple (Blue Icons)") },
{ "breeze_light", QObject::tr("Breeze Light") },
{ "breeze_dark", QObject::tr("Breeze Dark") },
{ "OSX", QObject::tr("OSX") },
{ "iOS", QObject::tr("iOS") },
{ "flat", QObject::tr("Flat") },
{ "flat_white", QObject::tr("Flat (White)") },
{ "multimc", QObject::tr("Legacy") },
{ "custom", QObject::tr("Custom") } };
};