mirror of
https://github.com/UltimMC/Launcher.git
synced 2025-12-24 12:32:42 +00:00
@@ -451,6 +451,13 @@ set(FLAME_SOURCES
|
||||
modplatform/flame/FileResolvingTask.cpp
|
||||
)
|
||||
|
||||
set(MODPACKSCH_SOURCES
|
||||
modplatform/modpacksch/FTBPackInstallTask.h
|
||||
modplatform/modpacksch/FTBPackInstallTask.cpp
|
||||
modplatform/modpacksch/FTBPackManifest.h
|
||||
modplatform/modpacksch/FTBPackManifest.cpp
|
||||
)
|
||||
|
||||
add_unit_test(Index
|
||||
SOURCES meta/Index_test.cpp
|
||||
LIBS MultiMC_logic
|
||||
@@ -481,6 +488,7 @@ set(LOGIC_SOURCES
|
||||
${ICONS_SOURCES}
|
||||
${FTB_SOURCES}
|
||||
${FLAME_SOURCES}
|
||||
${MODPACKSCH_SOURCES}
|
||||
)
|
||||
|
||||
add_library(MultiMC_logic SHARED ${LOGIC_SOURCES})
|
||||
|
||||
@@ -97,6 +97,7 @@ void Env::initHttpMetaCache()
|
||||
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
||||
m_metacache->addBase("general", QDir("cache").absolutePath());
|
||||
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
|
||||
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
|
||||
m_metacache->addBase("TwitchPacks", QDir("cache/TwitchPacks").absolutePath());
|
||||
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
||||
m_metacache->addBase("root", QDir::currentPath());
|
||||
|
||||
@@ -115,7 +115,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
}
|
||||
}
|
||||
|
||||
if(!results.contains("os.arch") || !results.contains("java.version") || !success)
|
||||
if(!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success)
|
||||
{
|
||||
result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
|
||||
emit checkFinished(result);
|
||||
@@ -124,6 +124,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
|
||||
auto os_arch = results["os.arch"];
|
||||
auto java_version = results["java.version"];
|
||||
auto java_vendor = results["java.vendor"];
|
||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64";
|
||||
|
||||
|
||||
@@ -132,6 +133,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
result.mojangPlatform = is_64 ? "64" : "32";
|
||||
result.realPlatform = os_arch;
|
||||
result.javaVersion = java_version;
|
||||
result.javaVendor = java_vendor;
|
||||
qDebug() << "Java checker succeeded.";
|
||||
emit checkFinished(result);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ struct MULTIMC_LOGIC_EXPORT JavaCheckResult
|
||||
QString mojangPlatform;
|
||||
QString realPlatform;
|
||||
JavaVersion javaVersion;
|
||||
QString javaVendor;
|
||||
QString outLog;
|
||||
QString errorLog;
|
||||
bool is_64bit = false;
|
||||
|
||||
@@ -33,17 +33,17 @@ void CheckJava::executeTask()
|
||||
if (perInstance)
|
||||
{
|
||||
emit logLine(
|
||||
tr("The java binary \"%1\" couldn't be found. Please fix the java path "
|
||||
QString("The java binary \"%1\" couldn't be found. Please fix the java path "
|
||||
"override in the instance's settings or disable it.").arg(m_javaPath),
|
||||
MessageLevel::Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in "
|
||||
emit logLine(QString("The java binary \"%1\" couldn't be found. Please set up java in "
|
||||
"the settings.").arg(m_javaPath),
|
||||
MessageLevel::Warning);
|
||||
}
|
||||
emitFailed(tr("Java path is not valid."));
|
||||
emitFailed(QString("Java path is not valid."));
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -56,12 +56,13 @@ void CheckJava::executeTask()
|
||||
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
|
||||
auto storedArchitecture = settings->get("JavaArchitecture").toString();
|
||||
auto storedVersion = settings->get("JavaVersion").toString();
|
||||
auto storedVendor = settings->get("JavaVendor").toString();
|
||||
m_javaUnixTime = javaUnixTime;
|
||||
// if timestamps are not the same, or something is missing, check!
|
||||
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
|
||||
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0 || storedVendor.size() == 0)
|
||||
{
|
||||
m_JavaChecker = new JavaChecker();
|
||||
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
|
||||
emit logLine(QString("Checking Java version..."), MessageLevel::MultiMC);
|
||||
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
|
||||
m_JavaChecker->m_path = realJavaPath;
|
||||
m_JavaChecker->performCheck();
|
||||
@@ -71,7 +72,8 @@ void CheckJava::executeTask()
|
||||
{
|
||||
auto verString = instance->settings()->get("JavaVersion").toString();
|
||||
auto archString = instance->settings()->get("JavaArchitecture").toString();
|
||||
printJavaInfo(verString, archString);
|
||||
auto vendorString = instance->settings()->get("JavaVendor").toString();
|
||||
printJavaInfo(verString, archString, vendorString);
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
@@ -83,16 +85,16 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
case JavaCheckResult::Validity::Errored:
|
||||
{
|
||||
// Error message displayed if java can't start
|
||||
emit logLine(tr("Could not start java:"), MessageLevel::Error);
|
||||
emit logLine(QString("Could not start java:"), MessageLevel::Error);
|
||||
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
|
||||
emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
|
||||
printSystemInfo(false, false);
|
||||
emitFailed(tr("Could not start java!"));
|
||||
emitFailed(QString("Could not start java!"));
|
||||
return;
|
||||
}
|
||||
case JavaCheckResult::Validity::ReturnedInvalidData:
|
||||
{
|
||||
emit logLine(tr("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
|
||||
emit logLine(QString("Java checker returned some invalid data MultiMC doesn't understand:"), MessageLevel::Error);
|
||||
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
|
||||
emit logLine("\nMinecraft might not start properly.", MessageLevel::MultiMC);
|
||||
printSystemInfo(false, false);
|
||||
@@ -102,9 +104,10 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
case JavaCheckResult::Validity::Valid:
|
||||
{
|
||||
auto instance = m_parent->instance();
|
||||
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
|
||||
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.javaVendor);
|
||||
instance->settings()->set("JavaVersion", result.javaVersion.toString());
|
||||
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
|
||||
instance->settings()->set("JavaVendor", result.javaVendor);
|
||||
instance->settings()->set("JavaTimestamp", m_javaUnixTime);
|
||||
emitSucceeded();
|
||||
return;
|
||||
@@ -112,9 +115,9 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
}
|
||||
}
|
||||
|
||||
void CheckJava::printJavaInfo(const QString& version, const QString& architecture)
|
||||
void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString & vendor)
|
||||
{
|
||||
emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
|
||||
emit logLine(QString("Java is version %1, using %2-bit architecture, from %3.\n\n").arg(version, architecture, vendor), MessageLevel::MultiMC);
|
||||
printSystemInfo(true, architecture == "64");
|
||||
}
|
||||
|
||||
@@ -124,13 +127,13 @@ void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit)
|
||||
auto system64 = Sys::isSystem64bit();
|
||||
if(cpu64 != system64)
|
||||
{
|
||||
emit logLine(tr("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
|
||||
emit logLine(QString("Your CPU architecture is not matching your system architecture. You might want to install a 64bit Operating System.\n\n"), MessageLevel::Error);
|
||||
}
|
||||
if(javaIsKnown)
|
||||
{
|
||||
if(javaIs64bit != system64)
|
||||
{
|
||||
emit logLine(tr("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
|
||||
emit logLine(QString("Your Java architecture is not matching your system architecture. You might want to install a 64bit Java version.\n\n"), MessageLevel::Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ private slots:
|
||||
void checkJavaFinished(JavaCheckResult result);
|
||||
|
||||
private:
|
||||
void printJavaInfo(const QString & version, const QString & architecture);
|
||||
void printJavaInfo(const QString & version, const QString & architecture, const QString & vendor);
|
||||
void printSystemInfo(bool javaIsKnown, bool javaIs64bit);
|
||||
|
||||
private:
|
||||
|
||||
@@ -100,6 +100,11 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
|
||||
auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride);
|
||||
|
||||
// Native library workarounds
|
||||
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(globalSettings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||
|
||||
// DEPRECATED: Read what versions the user configuration thinks should be used
|
||||
m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
|
||||
m_settings->registerSetting("LWJGLVersion", "");
|
||||
|
||||
@@ -138,14 +138,31 @@ void World::repath(const QFileInfo &file)
|
||||
m_folderName = file.fileName();
|
||||
if(file.isFile() && file.suffix() == "zip")
|
||||
{
|
||||
m_iconFile = QString();
|
||||
readFromZip(file);
|
||||
}
|
||||
else if(file.isDir())
|
||||
{
|
||||
QFileInfo assumedIconPath(file.absoluteFilePath() + "/icon.png");
|
||||
if(assumedIconPath.exists()) {
|
||||
m_iconFile = assumedIconPath.absoluteFilePath();
|
||||
}
|
||||
readFromFS(file);
|
||||
}
|
||||
}
|
||||
|
||||
bool World::resetIcon()
|
||||
{
|
||||
if(m_iconFile.isNull()) {
|
||||
return false;
|
||||
}
|
||||
if(QFile(m_iconFile).remove()) {
|
||||
m_iconFile = QString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void World::readFromFS(const QFileInfo &file)
|
||||
{
|
||||
auto bytes = getLevelDatDataFromFS(file);
|
||||
|
||||
@@ -40,6 +40,10 @@ public:
|
||||
{
|
||||
return m_actualName;
|
||||
}
|
||||
QString iconFile() const
|
||||
{
|
||||
return m_iconFile;
|
||||
}
|
||||
QDateTime lastPlayed() const
|
||||
{
|
||||
return m_lastPlayed;
|
||||
@@ -70,6 +74,8 @@ public:
|
||||
bool replace(World &with);
|
||||
// change the world's filesystem path (used by world lists for *MAGIC* purposes)
|
||||
void repath(const QFileInfo &file);
|
||||
// remove the icon file, if any
|
||||
bool resetIcon();
|
||||
|
||||
bool rename(const QString &to);
|
||||
bool install(const QString &to, const QString &name= QString());
|
||||
@@ -88,6 +94,7 @@ protected:
|
||||
QString m_containerOffsetPath;
|
||||
QString m_folderName;
|
||||
QString m_actualName;
|
||||
QString m_iconFile;
|
||||
QDateTime levelDatTime;
|
||||
QDateTime m_lastPlayed;
|
||||
int64_t m_randomSeed = 0;
|
||||
|
||||
@@ -136,6 +136,19 @@ bool WorldList::deleteWorlds(int first, int last)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldList::resetIcon(int row)
|
||||
{
|
||||
if (row >= worlds.size() || row < 0)
|
||||
return false;
|
||||
World &m = worlds[row];
|
||||
if(m.resetIcon()) {
|
||||
emit dataChanged(index(row), index(row), {WorldList::IconFileRole});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int WorldList::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return 3;
|
||||
@@ -195,6 +208,10 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
return world.lastPlayed();
|
||||
}
|
||||
case IconFileRole:
|
||||
{
|
||||
return world.iconFile();
|
||||
}
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ public:
|
||||
SeedRole,
|
||||
NameRole,
|
||||
GameModeRole,
|
||||
LastPlayedRole
|
||||
LastPlayedRole,
|
||||
IconFileRole
|
||||
};
|
||||
|
||||
WorldList(const QString &dir);
|
||||
@@ -81,6 +82,9 @@ public:
|
||||
/// Deletes the mod at the given index.
|
||||
virtual bool deleteWorld(int index);
|
||||
|
||||
/// Removes the world icon, if any
|
||||
virtual bool resetIcon(int index);
|
||||
|
||||
/// Deletes all the selected mods
|
||||
virtual bool deleteWorlds(int first, int last);
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ static QString replaceSuffix (QString target, const QString &suffix, const QStri
|
||||
return target + replacement;
|
||||
}
|
||||
|
||||
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack)
|
||||
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW)
|
||||
{
|
||||
QuaZip zip(source);
|
||||
if(!zip.open(QuaZip::mdUnzip))
|
||||
@@ -48,6 +48,13 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH
|
||||
do
|
||||
{
|
||||
QString name = zip.getCurrentFileName();
|
||||
auto lowercase = name.toLower();
|
||||
if (nativeGLFW && name.contains("glfw")) {
|
||||
continue;
|
||||
}
|
||||
if (nativeOpenAL && name.contains("openal")) {
|
||||
continue;
|
||||
}
|
||||
if(applyJnilibHack)
|
||||
{
|
||||
name = replaceSuffix(name, ".jnilib", ".dylib");
|
||||
@@ -76,12 +83,16 @@ void ExtractNatives::executeTask()
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto settings = minecraftInstance->settings();
|
||||
bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
|
||||
bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
|
||||
|
||||
auto outputPath = minecraftInstance->getNativePath();
|
||||
auto javaVersion = minecraftInstance->getJavaVersion();
|
||||
bool jniHackEnabled = javaVersion.major() >= 8;
|
||||
for(const auto &source: toExtract)
|
||||
{
|
||||
if(!unzipNatives(source, outputPath, jniHackEnabled))
|
||||
if(!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW))
|
||||
{
|
||||
auto reason = tr("Couldn't extract native jar '%1' to destination '%2'").arg(source, outputPath);
|
||||
emit logLine(reason, MessageLevel::Fatal);
|
||||
|
||||
150
api/logic/modplatform/modpacksch/FTBPackInstallTask.cpp
Normal file
150
api/logic/modplatform/modpacksch/FTBPackInstallTask.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "FTBPackInstallTask.h"
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
namespace ModpacksCH {
|
||||
|
||||
PackInstallTask::PackInstallTask(Modpack pack, QString version)
|
||||
{
|
||||
m_pack = pack;
|
||||
m_version_name = version;
|
||||
}
|
||||
|
||||
bool PackInstallTask::abort()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PackInstallTask::executeTask()
|
||||
{
|
||||
// Find pack version
|
||||
bool found = false;
|
||||
VersionInfo version;
|
||||
|
||||
for(auto vInfo : m_pack.versions) {
|
||||
if (vInfo.name == m_version_name) {
|
||||
found = true;
|
||||
version = vInfo;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
emitFailed("failed to find pack version " + m_version_name);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *netJob = new NetJob("ModpacksCH::VersionFetch");
|
||||
auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1/%2")
|
||||
.arg(m_pack.id).arg(version.id);
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
|
||||
QObject::connect(netJob, &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
|
||||
QObject::connect(netJob, &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
|
||||
}
|
||||
|
||||
void PackInstallTask::onDownloadSucceeded()
|
||||
{
|
||||
jobPtr.reset();
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
if(parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << response;
|
||||
return;
|
||||
}
|
||||
|
||||
auto obj = doc.object();
|
||||
|
||||
ModpacksCH::Version version;
|
||||
try
|
||||
{
|
||||
ModpacksCH::loadVersion(version, obj);
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return;
|
||||
}
|
||||
m_version = version;
|
||||
|
||||
install();
|
||||
}
|
||||
|
||||
void PackInstallTask::onDownloadFailed(QString reason)
|
||||
{
|
||||
jobPtr.reset();
|
||||
emitFailed(reason);
|
||||
}
|
||||
|
||||
void PackInstallTask::install()
|
||||
{
|
||||
setStatus(tr("Installing modpack"));
|
||||
|
||||
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
|
||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto components = instance.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
|
||||
for(auto target : m_version.targets) {
|
||||
if(target.type == "game" && target.name == "minecraft") {
|
||||
components->setComponentVersion("net.minecraft", target.version, true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto target : m_version.targets) {
|
||||
if(target.type == "modloader" && target.name == "forge") {
|
||||
components->setComponentVersion("net.minecraftforge", target.version, true);
|
||||
}
|
||||
}
|
||||
components->saveNow();
|
||||
|
||||
jobPtr.reset(new NetJob(tr("Mod download")));
|
||||
for(auto file : m_version.files) {
|
||||
if(file.serverOnly) continue;
|
||||
|
||||
auto relpath = FS::PathCombine("minecraft", file.path, file.name);
|
||||
auto path = FS::PathCombine(m_stagingPath , relpath);
|
||||
|
||||
qDebug() << "Will download" << file.url << "to" << path;
|
||||
auto dl = Net::Download::makeFile(file.url, path);
|
||||
jobPtr->addNetAction(dl);
|
||||
}
|
||||
connect(jobPtr.get(), &NetJob::succeeded, this, [&]()
|
||||
{
|
||||
jobPtr.reset();
|
||||
emitSucceeded();
|
||||
});
|
||||
|
||||
connect(jobPtr.get(), &NetJob::failed, [&](QString reason)
|
||||
{
|
||||
jobPtr.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
|
||||
setStatus(tr("Downloading mods..."));
|
||||
jobPtr->start();
|
||||
|
||||
instance.setName(m_instName);
|
||||
instance.setIconKey(m_instIcon);
|
||||
instanceSettings->resumeSave();
|
||||
}
|
||||
|
||||
}
|
||||
41
api/logic/modplatform/modpacksch/FTBPackInstallTask.h
Normal file
41
api/logic/modplatform/modpacksch/FTBPackInstallTask.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "FTBPackManifest.h"
|
||||
|
||||
#include "InstanceTask.h"
|
||||
#include "multimc_logic_export.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
namespace ModpacksCH {
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PackInstallTask(Modpack pack, QString version);
|
||||
virtual ~PackInstallTask(){}
|
||||
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void onDownloadSucceeded();
|
||||
void onDownloadFailed(QString reason);
|
||||
|
||||
private:
|
||||
void install();
|
||||
|
||||
private:
|
||||
NetJobPtr jobPtr;
|
||||
QByteArray response;
|
||||
|
||||
Modpack m_pack;
|
||||
QString m_version_name;
|
||||
Version m_version;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
156
api/logic/modplatform/modpacksch/FTBPackManifest.cpp
Normal file
156
api/logic/modplatform/modpacksch/FTBPackManifest.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "FTBPackManifest.h"
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
static void loadSpecs(ModpacksCH::Specs & s, QJsonObject & obj)
|
||||
{
|
||||
s.id = Json::requireInteger(obj, "id");
|
||||
s.minimum = Json::requireInteger(obj, "minimum");
|
||||
s.recommended = Json::requireInteger(obj, "recommended");
|
||||
}
|
||||
|
||||
static void loadTag(ModpacksCH::Tag & t, QJsonObject & obj)
|
||||
{
|
||||
t.id = Json::requireInteger(obj, "id");
|
||||
t.name = Json::requireString(obj, "name");
|
||||
}
|
||||
|
||||
static void loadArt(ModpacksCH::Art & a, QJsonObject & obj)
|
||||
{
|
||||
a.id = Json::requireInteger(obj, "id");
|
||||
a.url = Json::requireString(obj, "url");
|
||||
a.type = Json::requireString(obj, "type");
|
||||
a.width = Json::requireInteger(obj, "width");
|
||||
a.height = Json::requireInteger(obj, "height");
|
||||
a.compressed = Json::requireBoolean(obj, "compressed");
|
||||
a.sha1 = Json::requireString(obj, "sha1");
|
||||
a.size = Json::requireInteger(obj, "size");
|
||||
a.updated = Json::requireInteger(obj, "updated");
|
||||
}
|
||||
|
||||
static void loadAuthor(ModpacksCH::Author & a, QJsonObject & obj)
|
||||
{
|
||||
a.id = Json::requireInteger(obj, "id");
|
||||
a.name = Json::requireString(obj, "name");
|
||||
a.type = Json::requireString(obj, "type");
|
||||
a.website = Json::requireString(obj, "website");
|
||||
a.updated = Json::requireInteger(obj, "updated");
|
||||
}
|
||||
|
||||
static void loadVersionInfo(ModpacksCH::VersionInfo & v, QJsonObject & obj)
|
||||
{
|
||||
v.id = Json::requireInteger(obj, "id");
|
||||
v.name = Json::requireString(obj, "name");
|
||||
v.type = Json::requireString(obj, "type");
|
||||
v.updated = Json::requireInteger(obj, "updated");
|
||||
auto specs = Json::requireObject(obj, "specs");
|
||||
loadSpecs(v.specs, specs);
|
||||
}
|
||||
|
||||
void ModpacksCH::loadModpack(ModpacksCH::Modpack & m, QJsonObject & obj)
|
||||
{
|
||||
m.id = Json::requireInteger(obj, "id");
|
||||
m.name = Json::requireString(obj, "name");
|
||||
m.synopsis = Json::requireString(obj, "synopsis");
|
||||
m.description = Json::requireString(obj, "description");
|
||||
m.type = Json::requireString(obj, "type");
|
||||
m.featured = Json::requireBoolean(obj, "featured");
|
||||
m.installs = Json::requireInteger(obj, "installs");
|
||||
m.plays = Json::requireInteger(obj, "plays");
|
||||
m.updated = Json::requireInteger(obj, "updated");
|
||||
m.refreshed = Json::requireInteger(obj, "refreshed");
|
||||
auto artArr = Json::requireArray(obj, "art");
|
||||
for (const auto & artRaw : artArr)
|
||||
{
|
||||
auto artObj = Json::requireObject(artRaw);
|
||||
ModpacksCH::Art art;
|
||||
loadArt(art, artObj);
|
||||
m.art.append(art);
|
||||
}
|
||||
auto authorArr = Json::requireArray(obj, "authors");
|
||||
for (const auto & authorRaw : authorArr)
|
||||
{
|
||||
auto authorObj = Json::requireObject(authorRaw);
|
||||
ModpacksCH::Author author;
|
||||
loadAuthor(author, authorObj);
|
||||
m.authors.append(author);
|
||||
}
|
||||
auto versionArr = Json::requireArray(obj, "versions");
|
||||
for (const auto & versionRaw : versionArr)
|
||||
{
|
||||
auto versionObj = Json::requireObject(versionRaw);
|
||||
ModpacksCH::VersionInfo version;
|
||||
loadVersionInfo(version, versionObj);
|
||||
m.versions.append(version);
|
||||
}
|
||||
auto tagArr = Json::requireArray(obj, "tags");
|
||||
for (const auto & tagRaw : tagArr)
|
||||
{
|
||||
auto tagObj = Json::requireObject(tagRaw);
|
||||
ModpacksCH::Tag tag;
|
||||
loadTag(tag, tagObj);
|
||||
m.tags.append(tag);
|
||||
}
|
||||
m.updated = Json::requireInteger(obj, "updated");
|
||||
}
|
||||
|
||||
static void loadVersionTarget(ModpacksCH::VersionTarget & a, QJsonObject & obj)
|
||||
{
|
||||
a.id = Json::requireInteger(obj, "id");
|
||||
a.name = Json::requireString(obj, "name");
|
||||
a.type = Json::requireString(obj, "type");
|
||||
a.version = Json::requireString(obj, "version");
|
||||
a.updated = Json::requireInteger(obj, "updated");
|
||||
}
|
||||
|
||||
static void loadVersionFile(ModpacksCH::VersionFile & a, QJsonObject & obj)
|
||||
{
|
||||
a.id = Json::requireInteger(obj, "id");
|
||||
a.type = Json::requireString(obj, "type");
|
||||
a.path = Json::requireString(obj, "path");
|
||||
a.name = Json::requireString(obj, "name");
|
||||
a.version = Json::requireString(obj, "version");
|
||||
a.url = Json::requireString(obj, "url");
|
||||
a.sha1 = Json::requireString(obj, "sha1");
|
||||
a.size = Json::requireInteger(obj, "size");
|
||||
a.clientOnly = Json::requireBoolean(obj, "clientonly");
|
||||
a.serverOnly = Json::requireBoolean(obj, "serveronly");
|
||||
a.optional = Json::requireBoolean(obj, "optional");
|
||||
a.updated = Json::requireInteger(obj, "updated");
|
||||
}
|
||||
|
||||
void ModpacksCH::loadVersion(ModpacksCH::Version & m, QJsonObject & obj)
|
||||
{
|
||||
m.id = Json::requireInteger(obj, "id");
|
||||
m.parent = Json::requireInteger(obj, "parent");
|
||||
m.name = Json::requireString(obj, "name");
|
||||
m.type = Json::requireString(obj, "type");
|
||||
m.installs = Json::requireInteger(obj, "installs");
|
||||
m.plays = Json::requireInteger(obj, "plays");
|
||||
m.updated = Json::requireInteger(obj, "updated");
|
||||
m.refreshed = Json::requireInteger(obj, "refreshed");
|
||||
auto specs = Json::requireObject(obj, "specs");
|
||||
loadSpecs(m.specs, specs);
|
||||
auto targetArr = Json::requireArray(obj, "targets");
|
||||
for (const auto & targetRaw : targetArr)
|
||||
{
|
||||
auto versionObj = Json::requireObject(targetRaw);
|
||||
ModpacksCH::VersionTarget target;
|
||||
loadVersionTarget(target, versionObj);
|
||||
m.targets.append(target);
|
||||
}
|
||||
auto fileArr = Json::requireArray(obj, "files");
|
||||
for (const auto & fileRaw : fileArr)
|
||||
{
|
||||
auto fileObj = Json::requireObject(fileRaw);
|
||||
ModpacksCH::VersionFile file;
|
||||
loadVersionFile(file, fileObj);
|
||||
m.files.append(file);
|
||||
}
|
||||
}
|
||||
|
||||
//static void loadVersionChangelog(ModpacksCH::VersionChangelog & m, QJsonObject & obj)
|
||||
//{
|
||||
// m.content = Json::requireString(obj, "content");
|
||||
// m.updated = Json::requireInteger(obj, "updated");
|
||||
//}
|
||||
127
api/logic/modplatform/modpacksch/FTBPackManifest.h
Normal file
127
api/logic/modplatform/modpacksch/FTBPackManifest.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QUrl>
|
||||
#include <QJsonObject>
|
||||
#include <QMetaType>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
namespace ModpacksCH
|
||||
{
|
||||
|
||||
struct Specs
|
||||
{
|
||||
int id;
|
||||
int minimum;
|
||||
int recommended;
|
||||
};
|
||||
|
||||
struct Tag
|
||||
{
|
||||
int id;
|
||||
QString name;
|
||||
};
|
||||
|
||||
struct Art
|
||||
{
|
||||
int id;
|
||||
QString url;
|
||||
QString type;
|
||||
int width;
|
||||
int height;
|
||||
bool compressed;
|
||||
QString sha1;
|
||||
int size;
|
||||
int64_t updated;
|
||||
};
|
||||
|
||||
struct Author
|
||||
{
|
||||
int id;
|
||||
QString name;
|
||||
QString type;
|
||||
QString website;
|
||||
int64_t updated;
|
||||
};
|
||||
|
||||
struct VersionInfo
|
||||
{
|
||||
int id;
|
||||
QString name;
|
||||
QString type;
|
||||
int64_t updated;
|
||||
Specs specs;
|
||||
};
|
||||
|
||||
struct Modpack
|
||||
{
|
||||
int id;
|
||||
QString name;
|
||||
QString synopsis;
|
||||
QString description;
|
||||
QString type;
|
||||
bool featured;
|
||||
int installs;
|
||||
int plays;
|
||||
int64_t updated;
|
||||
int64_t refreshed;
|
||||
QVector<Art> art;
|
||||
QVector<Author> authors;
|
||||
QVector<VersionInfo> versions;
|
||||
QVector<Tag> tags;
|
||||
};
|
||||
|
||||
struct VersionTarget
|
||||
{
|
||||
int id;
|
||||
QString type;
|
||||
QString name;
|
||||
QString version;
|
||||
int64_t updated;
|
||||
};
|
||||
|
||||
struct VersionFile
|
||||
{
|
||||
int id;
|
||||
QString type;
|
||||
QString path;
|
||||
QString name;
|
||||
QString version;
|
||||
QString url;
|
||||
QString sha1;
|
||||
int size;
|
||||
bool clientOnly;
|
||||
bool serverOnly;
|
||||
bool optional;
|
||||
int64_t updated;
|
||||
};
|
||||
|
||||
struct Version
|
||||
{
|
||||
int id;
|
||||
int parent;
|
||||
QString name;
|
||||
QString type;
|
||||
int installs;
|
||||
int plays;
|
||||
int64_t updated;
|
||||
int64_t refreshed;
|
||||
Specs specs;
|
||||
QVector<VersionTarget> targets;
|
||||
QVector<VersionFile> files;
|
||||
};
|
||||
|
||||
struct VersionChangelog
|
||||
{
|
||||
QString content;
|
||||
int64_t updated;
|
||||
};
|
||||
|
||||
MULTIMC_LOGIC_EXPORT void loadModpack(Modpack & m, QJsonObject & obj);
|
||||
|
||||
MULTIMC_LOGIC_EXPORT void loadVersion(Version & m, QJsonObject & obj);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(ModpacksCH::Modpack)
|
||||
@@ -124,6 +124,10 @@ SET(MULTIMC_SOURCES
|
||||
# GUI - platform pages
|
||||
pages/modplatform/VanillaPage.cpp
|
||||
pages/modplatform/VanillaPage.h
|
||||
pages/modplatform/ftb/FtbModel.cpp
|
||||
pages/modplatform/ftb/FtbModel.h
|
||||
pages/modplatform/ftb/FtbPage.cpp
|
||||
pages/modplatform/ftb/FtbPage.h
|
||||
pages/modplatform/legacy_ftb/Page.cpp
|
||||
pages/modplatform/legacy_ftb/Page.h
|
||||
pages/modplatform/legacy_ftb/ListModel.h
|
||||
@@ -250,6 +254,7 @@ SET(MULTIMC_UIS
|
||||
|
||||
# Platform pages
|
||||
pages/modplatform/VanillaPage.ui
|
||||
pages/modplatform/ftb/FtbPage.ui
|
||||
pages/modplatform/legacy_ftb/Page.ui
|
||||
pages/modplatform/twitch/TwitchPage.ui
|
||||
pages/modplatform/ImportPage.ui
|
||||
|
||||
@@ -24,7 +24,8 @@ void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr("Java test succeeded!<br />Platform reported: %1<br />Java version "
|
||||
"reported: %2<br />").arg(result.realPlatform, result.javaVersion.toString());
|
||||
"reported: %2<br />Java vendor "
|
||||
"reported: %3<br />").arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor);
|
||||
if (result.errorLog.size())
|
||||
{
|
||||
auto htmlError = result.errorLog;
|
||||
|
||||
@@ -505,9 +505,14 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("JavaTimestamp", 0);
|
||||
m_settings->registerSetting("JavaArchitecture", "");
|
||||
m_settings->registerSetting("JavaVersion", "");
|
||||
m_settings->registerSetting("JavaVendor", "");
|
||||
m_settings->registerSetting("LastHostname", "");
|
||||
m_settings->registerSetting("JvmArgs", "");
|
||||
|
||||
// Native library workarounds
|
||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||
m_settings->registerSetting("UseNativeGLFW", false);
|
||||
|
||||
// Minecraft launch method
|
||||
m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "widgets/PageContainer.h"
|
||||
#include <pages/modplatform/VanillaPage.h>
|
||||
#include <pages/modplatform/ftb/FtbPage.h>
|
||||
#include <pages/modplatform/legacy_ftb/Page.h>
|
||||
#include <pages/modplatform/twitch/TwitchPage.h>
|
||||
#include <pages/modplatform/ImportPage.h>
|
||||
@@ -125,6 +126,7 @@ QList<BasePage *> NewInstanceDialog::getPages()
|
||||
{
|
||||
new VanillaPage(this),
|
||||
importPage,
|
||||
new FtbPage(this),
|
||||
new LegacyFTB::Page(this),
|
||||
twitchPage
|
||||
};
|
||||
|
||||
@@ -63,6 +63,10 @@ void MinecraftPage::applySettings()
|
||||
s->set("LaunchMaximized", ui->maximizedCheckBox->isChecked());
|
||||
s->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
|
||||
s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
|
||||
|
||||
// Native library workarounds
|
||||
s->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked());
|
||||
s->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked());
|
||||
}
|
||||
|
||||
void MinecraftPage::loadSettings()
|
||||
@@ -73,4 +77,7 @@ void MinecraftPage::loadSettings()
|
||||
ui->maximizedCheckBox->setChecked(s->get("LaunchMaximized").toBool());
|
||||
ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt());
|
||||
ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt());
|
||||
|
||||
ui->useNativeOpenALCheck->setChecked(s->get("UseNativeOpenAL").toBool());
|
||||
ui->useNativeGLFWCheck->setChecked(s->get("UseNativeGLFW").toBool());
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>545</width>
|
||||
<height>195</height>
|
||||
<width>936</width>
|
||||
<height>1134</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -111,6 +111,29 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
|
||||
<property name="title">
|
||||
<string>Native library workarounds</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of GLFW</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of OpenAL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacerMinecraft">
|
||||
<property name="orientation">
|
||||
@@ -135,6 +158,8 @@
|
||||
<tabstop>maximizedCheckBox</tabstop>
|
||||
<tabstop>windowWidthSpinBox</tabstop>
|
||||
<tabstop>windowHeightSpinBox</tabstop>
|
||||
<tabstop>useNativeGLFWCheck</tabstop>
|
||||
<tabstop>useNativeOpenALCheck</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -163,6 +163,20 @@ void InstanceSettingsPage::applySettings()
|
||||
m_settings->reset("WrapperCommand");
|
||||
m_settings->reset("PostExitCommand");
|
||||
}
|
||||
|
||||
// Workarounds
|
||||
bool workarounds = ui->nativeWorkaroundsGroupBox->isChecked();
|
||||
m_settings->set("OverrideNativeWorkarounds", workarounds);
|
||||
if(workarounds)
|
||||
{
|
||||
m_settings->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked());
|
||||
m_settings->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings->reset("UseNativeOpenAL");
|
||||
m_settings->reset("UseNativeGLFW");
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceSettingsPage::loadSettings()
|
||||
@@ -219,6 +233,11 @@ void InstanceSettingsPage::loadSettings()
|
||||
m_settings->get("WrapperCommand").toString(),
|
||||
m_settings->get("PostExitCommand").toString()
|
||||
);
|
||||
|
||||
// Workarounds
|
||||
ui->nativeWorkaroundsGroupBox->setChecked(m_settings->get("OverrideNativeWorkarounds").toBool());
|
||||
ui->useNativeGLFWCheck->setChecked(m_settings->get("UseNativeGLFW").toBool());
|
||||
ui->useNativeOpenALCheck->setChecked(m_settings->get("UseNativeOpenAL").toBool());
|
||||
}
|
||||
|
||||
void InstanceSettingsPage::on_javaDetectBtn_clicked()
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>738</width>
|
||||
<height>804</height>
|
||||
<width>691</width>
|
||||
<height>581</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -364,6 +364,58 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="workaroundsPage">
|
||||
<attribute name="title">
|
||||
<string>Workarounds</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="nativeWorkaroundsGroupBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Native libraries</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of GLFW</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
||||
<property name="text">
|
||||
<string>Use system installation of OpenAL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -398,6 +450,9 @@
|
||||
<tabstop>showConsoleCheck</tabstop>
|
||||
<tabstop>autoCloseConsoleCheck</tabstop>
|
||||
<tabstop>showConsoleErrorCheck</tabstop>
|
||||
<tabstop>nativeWorkaroundsGroupBox</tabstop>
|
||||
<tabstop>useNativeGLFWCheck</tabstop>
|
||||
<tabstop>useNativeOpenALCheck</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -31,6 +31,33 @@
|
||||
#include <QProcess>
|
||||
#include <FileSystem.h>
|
||||
|
||||
class WorldListProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WorldListProxyModel(QObject *parent) : QSortFilterProxyModel(parent) {}
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
|
||||
{
|
||||
QModelIndex sourceIndex = mapToSource(index);
|
||||
|
||||
if (index.column() == 0 && role == Qt::DecorationRole)
|
||||
{
|
||||
WorldList *worlds = qobject_cast<WorldList *>(sourceModel());
|
||||
auto iconFile = worlds->data(sourceIndex, WorldList::IconFileRole).toString();
|
||||
if(iconFile.isNull()) {
|
||||
// NOTE: Minecraft uses the same placeholder for servers AND worlds
|
||||
return MMC->getThemedIcon("unknown_server");
|
||||
}
|
||||
return QIcon(iconFile);
|
||||
}
|
||||
|
||||
return sourceIndex.data(role);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr<WorldList> worlds, QWidget *parent)
|
||||
: QMainWindow(parent), m_inst(inst), ui(new Ui::WorldListPage), m_worlds(worlds)
|
||||
{
|
||||
@@ -38,13 +65,14 @@ WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr<WorldList> worl
|
||||
|
||||
ui->toolBar->insertSpacer(ui->actionRefresh);
|
||||
|
||||
QSortFilterProxyModel * proxy = new QSortFilterProxyModel(this);
|
||||
WorldListProxyModel * proxy = new WorldListProxyModel(this);
|
||||
proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
proxy->setSourceModel(m_worlds.get());
|
||||
ui->worldTreeView->setSortingEnabled(true);
|
||||
ui->worldTreeView->setModel(proxy);
|
||||
ui->worldTreeView->installEventFilter(this);
|
||||
ui->worldTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
ui->worldTreeView->setIconSize(QSize(64,64));
|
||||
connect(ui->worldTreeView, &QTreeView::customContextMenuRequested, this, &WorldListPage::ShowContextMenu);
|
||||
|
||||
auto head = ui->worldTreeView->header();
|
||||
@@ -142,6 +170,19 @@ void WorldListPage::on_actionView_Folder_triggered()
|
||||
DesktopServices::openDirectory(m_worlds->dir().absolutePath(), true);
|
||||
}
|
||||
|
||||
void WorldListPage::on_actionReset_Icon_triggered()
|
||||
{
|
||||
auto proxiedIndex = getSelectedWorld();
|
||||
|
||||
if(!proxiedIndex.isValid())
|
||||
return;
|
||||
|
||||
if(m_worlds->resetIcon(proxiedIndex.row())) {
|
||||
ui->actionReset_Icon->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QModelIndex WorldListPage::getSelectedWorld()
|
||||
{
|
||||
auto index = ui->worldTreeView->selectionModel()->currentIndex();
|
||||
@@ -255,6 +296,8 @@ void WorldListPage::worldChanged(const QModelIndex ¤t, const QModelIndex &
|
||||
ui->actionRemove->setEnabled(enable);
|
||||
ui->actionCopy->setEnabled(enable);
|
||||
ui->actionRename->setEnabled(enable);
|
||||
bool hasIcon = !index.data(WorldList::IconFileRole).isNull();
|
||||
ui->actionReset_Icon->setEnabled(enable && hasIcon);
|
||||
}
|
||||
|
||||
void WorldListPage::on_actionAdd_triggered()
|
||||
@@ -342,3 +385,5 @@ void WorldListPage::on_actionRefresh_triggered()
|
||||
{
|
||||
m_worlds->update();
|
||||
}
|
||||
|
||||
#include "WorldListPage.moc"
|
||||
|
||||
@@ -90,6 +90,7 @@ private slots:
|
||||
void on_actionRename_triggered();
|
||||
void on_actionRefresh_triggered();
|
||||
void on_actionView_Folder_triggered();
|
||||
void on_actionReset_Icon_triggered();
|
||||
void worldChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void mceditState(LoggedProcess::State state);
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -79,6 +85,7 @@
|
||||
<addaction name="actionCopy"/>
|
||||
<addaction name="actionRemove"/>
|
||||
<addaction name="actionMCEdit"/>
|
||||
<addaction name="actionReset_Icon"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCopy_Seed"/>
|
||||
<addaction name="actionRefresh"/>
|
||||
@@ -124,6 +131,14 @@
|
||||
<string>View Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReset_Icon">
|
||||
<property name="text">
|
||||
<string>Reset Icon</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove world icon to make the game re-generate it on next load.</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
||||
302
application/pages/modplatform/ftb/FtbModel.cpp
Normal file
302
application/pages/modplatform/ftb/FtbModel.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
#include "FtbModel.h"
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "Env.h"
|
||||
#include "MultiMC.h"
|
||||
#include "Json.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
namespace Ftb {
|
||||
|
||||
ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ListModel::~ListModel()
|
||||
{
|
||||
}
|
||||
|
||||
int ListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return modpacks.size();
|
||||
}
|
||||
|
||||
int ListModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant ListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
int pos = index.row();
|
||||
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
|
||||
{
|
||||
return QString("INVALID INDEX %1").arg(pos);
|
||||
}
|
||||
|
||||
ModpacksCH::Modpack pack = modpacks.at(pos);
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
return pack.name;
|
||||
}
|
||||
else if (role == Qt::ToolTipRole)
|
||||
{
|
||||
return pack.synopsis;
|
||||
}
|
||||
else if(role == Qt::DecorationRole)
|
||||
{
|
||||
QIcon placeholder = MMC->getThemedIcon("screenshot-placeholder");
|
||||
|
||||
auto iter = m_logoMap.find(pack.name);
|
||||
if (iter != m_logoMap.end()) {
|
||||
auto & logo = *iter;
|
||||
if(!logo.result.isNull()) {
|
||||
return logo.result;
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
for(auto art : pack.art) {
|
||||
if(art.type == "square") {
|
||||
((ListModel *)this)->requestLogo(pack.name, art.url);
|
||||
}
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
else if(role == Qt::UserRole)
|
||||
{
|
||||
QVariant v;
|
||||
v.setValue(pack);
|
||||
return v;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void ListModel::performSearch()
|
||||
{
|
||||
auto *netJob = new NetJob("Ftb::Search");
|
||||
QString searchUrl;
|
||||
if(currentSearchTerm.isEmpty()) {
|
||||
searchUrl = BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/popular/plays/100";
|
||||
}
|
||||
else {
|
||||
searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/search/25?term=%1")
|
||||
.arg(currentSearchTerm);
|
||||
}
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished);
|
||||
QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed);
|
||||
}
|
||||
|
||||
void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
|
||||
{
|
||||
if(m_logoMap.contains(logo))
|
||||
{
|
||||
callback(ENV.metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
requestLogo(logo, logoUrl);
|
||||
}
|
||||
}
|
||||
|
||||
void ListModel::searchWithTerm(const QString &term)
|
||||
{
|
||||
if(currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull()) {
|
||||
return;
|
||||
}
|
||||
currentSearchTerm = term;
|
||||
if(jobPtr) {
|
||||
jobPtr->abort();
|
||||
searchState = ResetRequested;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
endResetModel();
|
||||
searchState = None;
|
||||
}
|
||||
performSearch();
|
||||
}
|
||||
|
||||
void ListModel::searchRequestFinished()
|
||||
{
|
||||
jobPtr.reset();
|
||||
remainingPacks.clear();
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
if(parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << response;
|
||||
return;
|
||||
}
|
||||
|
||||
auto packs = doc.object().value("packs").toArray();
|
||||
for(auto pack : packs) {
|
||||
auto packId = pack.toInt();
|
||||
remainingPacks.append(packId);
|
||||
}
|
||||
|
||||
if(!remainingPacks.isEmpty()) {
|
||||
currentPack = remainingPacks.at(0);
|
||||
requestPack();
|
||||
}
|
||||
}
|
||||
|
||||
void ListModel::searchRequestFailed(QString reason)
|
||||
{
|
||||
jobPtr.reset();
|
||||
remainingPacks.clear();
|
||||
|
||||
if(searchState == ResetRequested) {
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
endResetModel();
|
||||
|
||||
performSearch();
|
||||
} else {
|
||||
searchState = Finished;
|
||||
}
|
||||
}
|
||||
|
||||
void ListModel::requestPack()
|
||||
{
|
||||
auto *netJob = new NetJob("Ftb::Search");
|
||||
auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1")
|
||||
.arg(currentPack);
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
|
||||
QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::packRequestFinished);
|
||||
QObject::connect(netJob, &NetJob::failed, this, &ListModel::packRequestFailed);
|
||||
}
|
||||
|
||||
void ListModel::packRequestFinished()
|
||||
{
|
||||
jobPtr.reset();
|
||||
remainingPacks.removeOne(currentPack);
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
|
||||
if(parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << response;
|
||||
return;
|
||||
}
|
||||
|
||||
auto obj = doc.object();
|
||||
|
||||
ModpacksCH::Modpack pack;
|
||||
try
|
||||
{
|
||||
ModpacksCH::loadModpack(pack, obj);
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
qDebug() << QString::fromUtf8(response);
|
||||
qWarning() << "Error while reading pack manifest from FTB: " << e.cause();
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size());
|
||||
modpacks.append(pack);
|
||||
endInsertRows();
|
||||
|
||||
if(!remainingPacks.isEmpty()) {
|
||||
currentPack = remainingPacks.at(0);
|
||||
requestPack();
|
||||
}
|
||||
}
|
||||
|
||||
void ListModel::packRequestFailed(QString reason)
|
||||
{
|
||||
jobPtr.reset();
|
||||
remainingPacks.removeOne(currentPack);
|
||||
}
|
||||
|
||||
void ListModel::logoLoaded(QString logo, bool stale)
|
||||
{
|
||||
auto & logoObj = m_logoMap[logo];
|
||||
logoObj.downloadJob.reset();
|
||||
QString smallPath = logoObj.fullpath + ".small";
|
||||
|
||||
QFileInfo smallInfo(smallPath);
|
||||
|
||||
if(stale || !smallInfo.exists()) {
|
||||
QImage image(logoObj.fullpath);
|
||||
if (image.isNull())
|
||||
{
|
||||
logoObj.failed = true;
|
||||
return;
|
||||
}
|
||||
QImage small;
|
||||
if (image.width() > image.height()) {
|
||||
small = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation);
|
||||
}
|
||||
else {
|
||||
small = image.scaledToHeight(512).scaledToHeight(256, Qt::SmoothTransformation);
|
||||
}
|
||||
QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
|
||||
QImage square(QSize(256, 256), QImage::Format_ARGB32);
|
||||
square.fill(Qt::transparent);
|
||||
|
||||
QPainter painter(&square);
|
||||
painter.drawImage(offset, small);
|
||||
painter.end();
|
||||
|
||||
square.save(logoObj.fullpath + ".small", "PNG");
|
||||
}
|
||||
|
||||
logoObj.result = QIcon(logoObj.fullpath + ".small");
|
||||
for(int i = 0; i < modpacks.size(); i++) {
|
||||
if(modpacks[i].name == logo) {
|
||||
emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListModel::logoFailed(QString logo)
|
||||
{
|
||||
m_logoMap[logo].failed = true;
|
||||
m_logoMap[logo].downloadJob.reset();
|
||||
}
|
||||
|
||||
void ListModel::requestLogo(QString logo, QString url)
|
||||
{
|
||||
if(m_logoMap.contains(logo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetaEntryPtr entry = ENV.metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
|
||||
|
||||
bool stale = entry->isStale();
|
||||
|
||||
NetJob *job = new NetJob(QString("FTB Icon Download %1").arg(logo));
|
||||
job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
|
||||
|
||||
auto fullPath = entry->getFullPath();
|
||||
QObject::connect(job, &NetJob::finished, this, [this, logo, fullPath, stale]
|
||||
{
|
||||
logoLoaded(logo, stale);
|
||||
});
|
||||
|
||||
QObject::connect(job, &NetJob::failed, this, [this, logo]
|
||||
{
|
||||
logoFailed(logo);
|
||||
});
|
||||
|
||||
auto &newLogoEntry = m_logoMap[logo];
|
||||
newLogoEntry.downloadJob = job;
|
||||
newLogoEntry.fullpath = fullPath;
|
||||
job->start();
|
||||
}
|
||||
|
||||
}
|
||||
68
application/pages/modplatform/ftb/FtbModel.h
Normal file
68
application/pages/modplatform/ftb/FtbModel.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "modplatform/modpacksch/FTBPackManifest.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <QIcon>
|
||||
|
||||
namespace Ftb {
|
||||
|
||||
struct Logo {
|
||||
QString fullpath;
|
||||
NetJobPtr downloadJob;
|
||||
QIcon result;
|
||||
bool failed = false;
|
||||
};
|
||||
|
||||
typedef QMap<QString, Logo> LogoMap;
|
||||
typedef std::function<void(QString)> LogoCallback;
|
||||
|
||||
class ListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListModel(QObject *parent);
|
||||
virtual ~ListModel();
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
|
||||
void searchWithTerm(const QString & term);
|
||||
|
||||
private slots:
|
||||
void performSearch();
|
||||
void searchRequestFinished();
|
||||
void searchRequestFailed(QString reason);
|
||||
|
||||
void requestPack();
|
||||
void packRequestFinished();
|
||||
void packRequestFailed(QString reason);
|
||||
|
||||
void logoFailed(QString logo);
|
||||
void logoLoaded(QString logo, bool stale);
|
||||
|
||||
private:
|
||||
void requestLogo(QString file, QString url);
|
||||
|
||||
private:
|
||||
QList<ModpacksCH::Modpack> modpacks;
|
||||
LogoMap m_logoMap;
|
||||
|
||||
QString currentSearchTerm;
|
||||
enum SearchState {
|
||||
None,
|
||||
CanPossiblyFetchMore,
|
||||
ResetRequested,
|
||||
Finished
|
||||
} searchState = None;
|
||||
NetJobPtr jobPtr;
|
||||
int currentPack;
|
||||
QList<int> remainingPacks;
|
||||
QByteArray response;
|
||||
};
|
||||
|
||||
}
|
||||
109
application/pages/modplatform/ftb/FtbPage.cpp
Normal file
109
application/pages/modplatform/ftb/FtbPage.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "FtbPage.h"
|
||||
#include "ui_FtbPage.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "dialogs/NewInstanceDialog.h"
|
||||
#include "modplatform/modpacksch/FTBPackInstallTask.h"
|
||||
|
||||
FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::FtbPage), dialog(dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->searchButton, &QPushButton::clicked, this, &FtbPage::triggerSearch);
|
||||
ui->searchEdit->installEventFilter(this);
|
||||
model = new Ftb::ListModel(this);
|
||||
ui->packView->setModel(model);
|
||||
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
|
||||
|
||||
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged);
|
||||
}
|
||||
|
||||
FtbPage::~FtbPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool FtbPage::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Return) {
|
||||
triggerSearch();
|
||||
keyEvent->accept();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
bool FtbPage::shouldDisplay() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FtbPage::openedImpl()
|
||||
{
|
||||
dialog->setSuggestedPack();
|
||||
triggerSearch();
|
||||
}
|
||||
|
||||
void FtbPage::suggestCurrent()
|
||||
{
|
||||
if(isOpened)
|
||||
{
|
||||
dialog->setSuggestedPack(selected.name, new ModpacksCH::PackInstallTask(selected, selectedVersion));
|
||||
|
||||
for(auto art : selected.art) {
|
||||
if(art.type == "square") {
|
||||
QString editedLogoName;
|
||||
editedLogoName = selected.name;
|
||||
|
||||
model->getLogo(selected.name, art.url, [this, editedLogoName](QString logo)
|
||||
{
|
||||
dialog->setSuggestedIconFromFile(logo + ".small", editedLogoName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FtbPage::triggerSearch()
|
||||
{
|
||||
model->searchWithTerm(ui->searchEdit->text());
|
||||
}
|
||||
|
||||
void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second)
|
||||
{
|
||||
ui->versionSelectionBox->clear();
|
||||
|
||||
if(!first.isValid())
|
||||
{
|
||||
if(isOpened)
|
||||
{
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
selected = model->data(first, Qt::UserRole).value<ModpacksCH::Modpack>();
|
||||
|
||||
// reverse foreach, so that the newest versions are first
|
||||
for (auto i = selected.versions.size(); i--;) {
|
||||
ui->versionSelectionBox->addItem(selected.versions.at(i).name);
|
||||
}
|
||||
|
||||
suggestCurrent();
|
||||
}
|
||||
|
||||
void FtbPage::onVersionSelectionChanged(QString data)
|
||||
{
|
||||
if(data.isNull() || data.isEmpty())
|
||||
{
|
||||
selectedVersion = "";
|
||||
return;
|
||||
}
|
||||
|
||||
selectedVersion = data;
|
||||
suggestCurrent();
|
||||
}
|
||||
77
application/pages/modplatform/ftb/FtbPage.h
Normal file
77
application/pages/modplatform/ftb/FtbPage.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Copyright 2013-2019 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 "FtbModel.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include "pages/BasePage.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class FtbPage;
|
||||
}
|
||||
|
||||
class NewInstanceDialog;
|
||||
|
||||
class FtbPage : public QWidget, public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FtbPage(NewInstanceDialog* dialog, QWidget *parent = 0);
|
||||
virtual ~FtbPage();
|
||||
virtual QString displayName() const override
|
||||
{
|
||||
return tr("FTB");
|
||||
}
|
||||
virtual QIcon icon() const override
|
||||
{
|
||||
return MMC->getThemedIcon("ftb_logo");
|
||||
}
|
||||
virtual QString id() const override
|
||||
{
|
||||
return "ftb";
|
||||
}
|
||||
virtual QString helpPage() const override
|
||||
{
|
||||
return "FTB-platform";
|
||||
}
|
||||
virtual bool shouldDisplay() const override;
|
||||
|
||||
void openedImpl() override;
|
||||
|
||||
bool eventFilter(QObject * watched, QEvent * event) override;
|
||||
|
||||
private:
|
||||
void suggestCurrent();
|
||||
|
||||
private slots:
|
||||
void triggerSearch();
|
||||
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
||||
void onVersionSelectionChanged(QString data);
|
||||
|
||||
private:
|
||||
Ui::FtbPage *ui = nullptr;
|
||||
NewInstanceDialog* dialog = nullptr;
|
||||
Ftb::ListModel* model = nullptr;
|
||||
|
||||
ModpacksCH::Modpack selected;
|
||||
QString selectedVersion;
|
||||
};
|
||||
64
application/pages/modplatform/ftb/FtbPage.ui
Normal file
64
application/pages/modplatform/ftb/FtbPage.ui
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FtbPage</class>
|
||||
<widget class="QWidget" name="FtbPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>875</width>
|
||||
<height>745</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QListView" name="packView">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="searchButton">
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="searchEdit"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0" rowminimumheight="0" columnminimumwidth="0,0">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="versionSelectionBox"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Version selected:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>searchEdit</tabstop>
|
||||
<tabstop>searchButton</tabstop>
|
||||
<tabstop>packView</tabstop>
|
||||
<tabstop>versionSelectionBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -75,6 +75,8 @@ public:
|
||||
QString FMLLIBS_FORGE_BASE_URL = "https://files.minecraftforge.net/fmllibs/";
|
||||
QString TRANSLATIONS_BASE_URL = "https://files.multimc.org/translations/";
|
||||
|
||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||
|
||||
QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import java.lang.Integer;
|
||||
|
||||
public class JavaCheck
|
||||
{
|
||||
private static final String[] keys = {"os.arch", "java.version"};
|
||||
private static final String[] keys = {"os.arch", "java.version", "java.vendor"};
|
||||
public static void main (String [] args)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
Reference in New Issue
Block a user