Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into feat/launcher-updater
This commit is contained in:
commit
6bbff310bc
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -53,18 +53,6 @@ jobs:
|
|||||||
msystem: clang64
|
msystem: clang64
|
||||||
vcvars_arch: 'amd64_x86'
|
vcvars_arch: 'amd64_x86'
|
||||||
|
|
||||||
- os: windows-2022
|
|
||||||
name: "Windows-MSVC-Legacy"
|
|
||||||
msystem: ''
|
|
||||||
architecture: 'win32'
|
|
||||||
vcvars_arch: 'amd64_x86'
|
|
||||||
qt_ver: 5
|
|
||||||
qt_host: windows
|
|
||||||
qt_arch: 'win32_msvc2019'
|
|
||||||
qt_version: '5.15.2'
|
|
||||||
qt_modules: ''
|
|
||||||
qt_tools: 'tools_openssl_x86'
|
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-MSVC"
|
name: "Windows-MSVC"
|
||||||
msystem: ''
|
msystem: ''
|
||||||
|
3
.github/workflows/trigger_release.yml
vendored
3
.github/workflows/trigger_release.yml
vendored
@ -96,9 +96,6 @@ jobs:
|
|||||||
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
||||||
PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
|
|
||||||
PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
|
|
||||||
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
|
|
||||||
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
|
||||||
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
|
||||||
|
12
flake.lock
generated
12
flake.lock
generated
@ -106,11 +106,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696661029,
|
"lastModified": 1697009197,
|
||||||
"narHash": "sha256-GIB5VTkvsDIqfMpdtuetOzpm64P8wm8nBSv5Eo8XM3Y=",
|
"narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2de1be5b51c3d6fa833f1c1f222dc867dd054b31",
|
"rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -153,11 +153,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696516544,
|
"lastModified": 1696846637,
|
||||||
"narHash": "sha256-8rKE8Je6twTNFRTGF63P9mE3lZIq917RAicdc4XJO80=",
|
"narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "66c352d33e0907239e4a69416334f64af2c685cc",
|
"rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -970,6 +970,9 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/modplatform/ImportPage.cpp
|
ui/pages/modplatform/ImportPage.cpp
|
||||||
ui/pages/modplatform/ImportPage.h
|
ui/pages/modplatform/ImportPage.h
|
||||||
|
|
||||||
|
ui/pages/modplatform/OptionalModDialog.cpp
|
||||||
|
ui/pages/modplatform/OptionalModDialog.h
|
||||||
|
|
||||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
|
ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
|
||||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.h
|
ui/pages/modplatform/modrinth/ModrinthResourceModels.h
|
||||||
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
|
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
|
||||||
@ -1141,6 +1144,7 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/pages/modplatform/legacy_ftb/Page.ui
|
ui/pages/modplatform/legacy_ftb/Page.ui
|
||||||
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
|
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
|
||||||
ui/pages/modplatform/ImportPage.ui
|
ui/pages/modplatform/ImportPage.ui
|
||||||
|
ui/pages/modplatform/OptionalModDialog.ui
|
||||||
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
||||||
ui/pages/modplatform/technic/TechnicPage.ui
|
ui/pages/modplatform/technic/TechnicPage.ui
|
||||||
ui/widgets/InstanceCardWidget.ui
|
ui/widgets/InstanceCardWidget.ui
|
||||||
|
@ -272,6 +272,28 @@ bool ensureFolderPathExists(QString foldernamepath)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool copyFileAttributes(QString src, QString dst)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
auto attrs = GetFileAttributesW(src.toStdWString().c_str());
|
||||||
|
if (attrs == INVALID_FILE_ATTRIBUTES)
|
||||||
|
return false;
|
||||||
|
return SetFileAttributesW(dst.toStdWString().c_str(), attrs);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// needs folders to exists
|
||||||
|
void copyFolderAttributes(QString src, QString dst, QString relative)
|
||||||
|
{
|
||||||
|
auto path = PathCombine(src, relative);
|
||||||
|
QDir dsrc(src);
|
||||||
|
while ((path = QFileInfo(path).path()).length() >= src.length()) {
|
||||||
|
auto dst_path = PathCombine(dst, dsrc.relativeFilePath(path));
|
||||||
|
copyFileAttributes(path, dst_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copies a directory and it's contents from src to dest
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
* @param offset subdirectory form src to copy to dest
|
* @param offset subdirectory form src to copy to dest
|
||||||
@ -310,6 +332,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
auto dst_path = PathCombine(dst, relative_dst_path);
|
auto dst_path = PathCombine(dst, relative_dst_path);
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
ensureFilePathExists(dst_path);
|
ensureFilePathExists(dst_path);
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
copyFolderAttributes(src, dst, relative_dst_path);
|
||||||
|
#endif
|
||||||
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
|
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -1018,8 +1018,7 @@ std::optional<ModPlatform::ModLoaderTypes> PackProfile::getSupportedModLoaders()
|
|||||||
// TODO: remove this or add version condition once Quilt drops official Fabric support
|
// TODO: remove this or add version condition once Quilt drops official Fabric support
|
||||||
if (loaders & ModPlatform::Quilt)
|
if (loaders & ModPlatform::Quilt)
|
||||||
loaders |= ModPlatform::Fabric;
|
loaders |= ModPlatform::Fabric;
|
||||||
// TODO: remove this or add version condition once NeoForge drops official Forge support
|
if (getComponentVersion("net.minecraft") == "1.20.1" && (loaders & ModPlatform::NeoForge))
|
||||||
if (loaders & ModPlatform::NeoForge)
|
|
||||||
loaders |= ModPlatform::Forge;
|
loaders |= ModPlatform::Forge;
|
||||||
return loaders;
|
return loaders;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ class Mod;
|
|||||||
class Metadata {
|
class Metadata {
|
||||||
public:
|
public:
|
||||||
using ModStruct = Packwiz::V1::Mod;
|
using ModStruct = Packwiz::V1::Mod;
|
||||||
|
using ModSide = Packwiz::V1::Side;
|
||||||
|
|
||||||
static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct
|
static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct
|
||||||
{
|
{
|
||||||
|
@ -122,7 +122,7 @@ void ModFolderLoadTask::getFromMetadata()
|
|||||||
auto metadata = Metadata::get(m_index_dir, entry);
|
auto metadata = Metadata::get(m_index_dir, entry);
|
||||||
|
|
||||||
if (!metadata.isValid()) {
|
if (!metadata.isValid()) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* mod = new Mod(m_mods_dir, metadata);
|
auto* mod = new Mod(m_mods_dir, metadata);
|
||||||
|
@ -137,6 +137,7 @@ struct IndexedPack {
|
|||||||
QString logoName;
|
QString logoName;
|
||||||
QString logoUrl;
|
QString logoUrl;
|
||||||
QString websiteUrl;
|
QString websiteUrl;
|
||||||
|
QString side;
|
||||||
|
|
||||||
bool versionsLoaded = false;
|
bool versionsLoaded = false;
|
||||||
QVector<IndexedVersion> versions;
|
QVector<IndexedVersion> versions;
|
||||||
|
@ -43,5 +43,5 @@ void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj)
|
|||||||
m.system = Json::ensureBoolean(obj, QString("system"), false);
|
m.system = Json::ensureBoolean(obj, QString("system"), false);
|
||||||
m.description = Json::ensureString(obj, "description", "");
|
m.description = Json::ensureString(obj, "description", "");
|
||||||
|
|
||||||
m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "");
|
m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png";
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
#include "minecraft/World.h"
|
#include "minecraft/World.h"
|
||||||
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
|
#include "ui/pages/modplatform/OptionalModDialog.h"
|
||||||
|
|
||||||
static const FlameAPI api;
|
static const FlameAPI api;
|
||||||
|
|
||||||
@ -509,13 +510,33 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||||||
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
||||||
{
|
{
|
||||||
m_files_job.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network()));
|
m_files_job.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network()));
|
||||||
for (const auto& result : m_mod_id_resolver->getResults().files) {
|
auto results = m_mod_id_resolver->getResults().files;
|
||||||
QString filename = result.fileName;
|
|
||||||
|
QStringList optionalFiles;
|
||||||
|
for (auto& result : results) {
|
||||||
if (!result.required) {
|
if (!result.required) {
|
||||||
filename += ".disabled";
|
optionalFiles << FS::PathCombine(result.targetFolder, result.fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
QStringList selectedOptionalMods;
|
||||||
|
if (!optionalFiles.empty()) {
|
||||||
|
OptionalModDialog optionalModDialog(m_parent, optionalFiles);
|
||||||
|
if (optionalModDialog.exec() == QDialog::Rejected) {
|
||||||
|
emitAborted();
|
||||||
|
loop.quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedOptionalMods = optionalModDialog.getResult();
|
||||||
|
}
|
||||||
|
for (const auto& result : results) {
|
||||||
|
auto relpath = FS::PathCombine(result.targetFolder, result.fileName);
|
||||||
|
if (!result.required && !selectedOptionalMods.contains(relpath)) {
|
||||||
|
relpath += ".disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
relpath = FS::PathCombine("minecraft", relpath);
|
||||||
auto path = FS::PathCombine(m_stagingPath, relpath);
|
auto path = FS::PathCombine(m_stagingPath, relpath);
|
||||||
|
|
||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include "FlamePackIndex.h"
|
#include "FlamePackIndex.h"
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
@ -9,8 +11,8 @@ void Flame::loadIndexedPack(Flame::IndexedPack& pack, QJsonObject& obj)
|
|||||||
pack.description = Json::ensureString(obj, "summary", "");
|
pack.description = Json::ensureString(obj, "summary", "");
|
||||||
|
|
||||||
auto logo = Json::requireObject(obj, "logo");
|
auto logo = Json::requireObject(obj, "logo");
|
||||||
pack.logoName = Json::requireString(logo, "title");
|
|
||||||
pack.logoUrl = Json::requireString(logo, "thumbnailUrl");
|
pack.logoUrl = Json::requireString(logo, "thumbnailUrl");
|
||||||
|
pack.logoName = Json::requireString(obj, "slug") + "." + QFileInfo(QUrl(pack.logoUrl).fileName()).suffix();
|
||||||
|
|
||||||
auto authors = Json::requireArray(obj, "authors");
|
auto authors = Json::requireArray(obj, "authors");
|
||||||
for (auto authorIter : authors) {
|
for (auto authorIter : authors) {
|
||||||
|
@ -48,7 +48,7 @@ struct File {
|
|||||||
|
|
||||||
int projectId = 0;
|
int projectId = 0;
|
||||||
int fileId = 0;
|
int fileId = 0;
|
||||||
// NOTE: the opposite to 'optional'. This is at the time of writing unused.
|
// NOTE: the opposite to 'optional'
|
||||||
bool required = true;
|
bool required = true;
|
||||||
QString hash;
|
QString hash;
|
||||||
// NOTE: only set on blocked files ! Empty otherwise.
|
// NOTE: only set on blocked files ! Empty otherwise.
|
||||||
|
@ -70,16 +70,18 @@ void PackInstallTask::downloadPack()
|
|||||||
setProgress(1, 4);
|
setProgress(1, 4);
|
||||||
setAbortable(false);
|
setAbortable(false);
|
||||||
|
|
||||||
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
auto path = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
||||||
|
auto entry = APPLICATION->metacache()->resolveEntry("FTBPacks", path);
|
||||||
|
entry->setStale(true);
|
||||||
|
archivePath = entry->getFullPath();
|
||||||
netJobContainer.reset(new NetJob("Download FTB Pack", m_network));
|
netJobContainer.reset(new NetJob("Download FTB Pack", m_network));
|
||||||
QString url;
|
QString url;
|
||||||
if (m_pack.type == PackType::Private) {
|
if (m_pack.type == PackType::Private) {
|
||||||
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(archivePath);
|
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(path);
|
||||||
} else {
|
} else {
|
||||||
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath);
|
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(path);
|
||||||
}
|
}
|
||||||
netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath));
|
netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry));
|
||||||
|
|
||||||
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
||||||
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "modplatform/helpers/OverrideUtils.h"
|
#include "modplatform/helpers/OverrideUtils.h"
|
||||||
|
|
||||||
|
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||||
#include "net/ChecksumValidator.h"
|
#include "net/ChecksumValidator.h"
|
||||||
|
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
@ -16,8 +17,10 @@
|
|||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
#include "ui/pages/modplatform/OptionalModDialog.h"
|
||||||
|
|
||||||
#include <QAbstractButton>
|
#include <QAbstractButton>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
bool ModrinthCreationTask::abort()
|
bool ModrinthCreationTask::abort()
|
||||||
{
|
{
|
||||||
@ -319,10 +322,10 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
||||||
bool had_optional = false;
|
std::vector<Modrinth::File> optionalFiles;
|
||||||
for (const auto& modInfo : jsonFiles) {
|
for (const auto& modInfo : jsonFiles) {
|
||||||
Modrinth::File file;
|
Modrinth::File file;
|
||||||
file.path = Json::requireString(modInfo, "path");
|
file.path = Json::requireString(modInfo, "path").replace("\\", "/");
|
||||||
|
|
||||||
auto env = Json::ensureObject(modInfo, "env");
|
auto env = Json::ensureObject(modInfo, "env");
|
||||||
// 'env' field is optional
|
// 'env' field is optional
|
||||||
@ -331,18 +334,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
|||||||
if (support == "unsupported") {
|
if (support == "unsupported") {
|
||||||
continue;
|
continue;
|
||||||
} else if (support == "optional") {
|
} else if (support == "optional") {
|
||||||
// TODO: Make a review dialog for choosing which ones the user wants!
|
file.required = false;
|
||||||
if (!had_optional && show_optional_dialog) {
|
|
||||||
had_optional = true;
|
|
||||||
auto info = CustomMessageBox::selectable(
|
|
||||||
m_parent, tr("Optional mod detected!"),
|
|
||||||
tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"),
|
|
||||||
QMessageBox::Information);
|
|
||||||
info->exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.path.endsWith(".jar"))
|
|
||||||
file.path += ".disabled";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,9 +377,29 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
files.push_back(file);
|
(file.required ? files : optionalFiles).push_back(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!optionalFiles.empty()) {
|
||||||
|
QStringList oFiles;
|
||||||
|
for (auto file : optionalFiles)
|
||||||
|
oFiles.push_back(file.path);
|
||||||
|
OptionalModDialog optionalModDialog(m_parent, oFiles);
|
||||||
|
if (optionalModDialog.exec() == QDialog::Rejected) {
|
||||||
|
emitAborted();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto selectedMods = optionalModDialog.getResult();
|
||||||
|
for (auto file : optionalFiles) {
|
||||||
|
if (selectedMods.contains(file.path)) {
|
||||||
|
file.required = true;
|
||||||
|
} else {
|
||||||
|
file.path += ".disabled";
|
||||||
|
}
|
||||||
|
files.push_back(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (set_internal_data) {
|
if (set_internal_data) {
|
||||||
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
|
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
|
||||||
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
|
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
|
||||||
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
|
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
|
||||||
@ -129,7 +130,8 @@ void ModrinthPackExportTask::collectHashes()
|
|||||||
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
|
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
|
||||||
sha1.addData(data);
|
sha1.addData(data);
|
||||||
|
|
||||||
ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size() };
|
ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size(),
|
||||||
|
mod->metadata()->side };
|
||||||
resolvedFiles[relative] = resolvedFile;
|
resolvedFiles[relative] = resolvedFile;
|
||||||
|
|
||||||
// nice! we've managed to resolve based on local metadata!
|
// nice! we've managed to resolve based on local metadata!
|
||||||
@ -272,22 +274,33 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
QString path = iterator.key();
|
QString path = iterator.key();
|
||||||
const ResolvedFile& value = iterator.value();
|
const ResolvedFile& value = iterator.value();
|
||||||
|
|
||||||
if (optionalFiles) {
|
QJsonObject env;
|
||||||
|
|
||||||
// detect disabled mod
|
// detect disabled mod
|
||||||
const QFileInfo pathInfo(path);
|
const QFileInfo pathInfo(path);
|
||||||
if (pathInfo.suffix() == "disabled") {
|
if (optionalFiles && pathInfo.suffix() == "disabled") {
|
||||||
// rename it
|
// rename it
|
||||||
path = pathInfo.dir().filePath(pathInfo.completeBaseName());
|
path = pathInfo.dir().filePath(pathInfo.completeBaseName());
|
||||||
// ...and make it optional
|
|
||||||
QJsonObject env;
|
|
||||||
env["client"] = "optional";
|
env["client"] = "optional";
|
||||||
env["server"] = "optional";
|
env["server"] = "optional";
|
||||||
|
} else {
|
||||||
|
env["client"] = "required";
|
||||||
|
env["server"] = "required";
|
||||||
|
}
|
||||||
|
switch (iterator->side) {
|
||||||
|
case Metadata::ModSide::ClientSide:
|
||||||
|
env["server"] = "unsupported";
|
||||||
|
break;
|
||||||
|
case Metadata::ModSide::ServerSide:
|
||||||
|
env["client"] = "unsupported";
|
||||||
|
break;
|
||||||
|
case Metadata::ModSide::UniversalSide:
|
||||||
|
break;
|
||||||
|
}
|
||||||
fileOut["env"] = env;
|
fileOut["env"] = env;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileOut["path"] = path;
|
fileOut["path"] = path;
|
||||||
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
fileOut["downloads"] = QJsonArray{ iterator->url };
|
||||||
|
|
||||||
QJsonObject hashes;
|
QJsonObject hashes;
|
||||||
hashes["sha1"] = value.sha1;
|
hashes["sha1"] = value.sha1;
|
||||||
|
@ -44,6 +44,7 @@ class ModrinthPackExportTask : public Task {
|
|||||||
struct ResolvedFile {
|
struct ResolvedFile {
|
||||||
QString sha1, sha512, url;
|
QString sha1, sha512, url;
|
||||||
qint64 size;
|
qint64 size;
|
||||||
|
Metadata::ModSide side;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const QStringList PREFIXES;
|
static const QStringList PREFIXES;
|
||||||
|
@ -27,6 +27,11 @@
|
|||||||
static ModrinthAPI api;
|
static ModrinthAPI api;
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||||
|
|
||||||
|
bool shouldDownloadOnSide(QString side)
|
||||||
|
{
|
||||||
|
return side == "required" || side == "optional";
|
||||||
|
}
|
||||||
|
|
||||||
// https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject
|
// https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject
|
||||||
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||||
{
|
{
|
||||||
@ -53,6 +58,17 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||||||
modAuthor.url = api.getAuthorURL(modAuthor.name);
|
modAuthor.url = api.getAuthorURL(modAuthor.name);
|
||||||
pack.authors.append(modAuthor);
|
pack.authors.append(modAuthor);
|
||||||
|
|
||||||
|
auto client = shouldDownloadOnSide(Json::ensureString(obj, "client_side"));
|
||||||
|
auto server = shouldDownloadOnSide(Json::ensureString(obj, "server_side"));
|
||||||
|
|
||||||
|
if (server && client) {
|
||||||
|
pack.side = "both";
|
||||||
|
} else if (server) {
|
||||||
|
pack.side = "server";
|
||||||
|
} else if (client) {
|
||||||
|
pack.side = "client";
|
||||||
|
}
|
||||||
|
|
||||||
// Modrinth can have more data than what's provided by the basic search :)
|
// Modrinth can have more data than what's provided by the basic search :)
|
||||||
pack.extraDataLoaded = false;
|
pack.extraDataLoaded = false;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ModrinthPackManifest.h"
|
#include "ModrinthPackManifest.h"
|
||||||
|
#include <QFileInfo>
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
@ -56,8 +57,8 @@ void loadIndexedPack(Modpack& pack, QJsonObject& obj)
|
|||||||
pack.description = Json::ensureString(obj, "description");
|
pack.description = Json::ensureString(obj, "description");
|
||||||
auto temp_author_name = Json::ensureString(obj, "author");
|
auto temp_author_name = Json::ensureString(obj, "author");
|
||||||
pack.author = std::make_tuple(temp_author_name, api.getAuthorURL(temp_author_name));
|
pack.author = std::make_tuple(temp_author_name, api.getAuthorURL(temp_author_name));
|
||||||
pack.iconName = QString("modrinth_%1").arg(Json::ensureString(obj, "slug"));
|
|
||||||
pack.iconUrl = Json::ensureString(obj, "icon_url");
|
pack.iconUrl = Json::ensureString(obj, "icon_url");
|
||||||
|
pack.iconName = QString("modrinth_%1.%2").arg(Json::ensureString(obj, "slug"), QFileInfo(pack.iconUrl.fileName()).suffix());
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadIndexedInfo(Modpack& pack, QJsonObject& obj)
|
void loadIndexedInfo(Modpack& pack, QJsonObject& obj)
|
||||||
@ -111,9 +112,8 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc)
|
|||||||
unsortedVersions.append(file);
|
unsortedVersions.append(file);
|
||||||
}
|
}
|
||||||
auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool {
|
auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool {
|
||||||
bool a_better_release = a.version_type <= b.version_type;
|
|
||||||
// dates are in RFC 3339 format
|
// dates are in RFC 3339 format
|
||||||
return a.date > b.date && a_better_release;
|
return a.date > b.date;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||||
|
@ -57,6 +57,7 @@ struct File {
|
|||||||
QCryptographicHash::Algorithm hashAlgorithm;
|
QCryptographicHash::Algorithm hashAlgorithm;
|
||||||
QByteArray hash;
|
QByteArray hash;
|
||||||
QQueue<QUrl> downloads;
|
QQueue<QUrl> downloads;
|
||||||
|
bool required = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DonationData {
|
struct DonationData {
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
@ -111,6 +113,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP
|
|||||||
mod.provider = mod_pack.provider;
|
mod.provider = mod_pack.provider;
|
||||||
mod.file_id = mod_version.fileId;
|
mod.file_id = mod_version.fileId;
|
||||||
mod.project_id = mod_pack.addonId;
|
mod.project_id = mod_pack.addonId;
|
||||||
|
mod.side = stringToSide(mod_pack.side);
|
||||||
|
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
@ -154,38 +157,52 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
|||||||
FS::ensureFilePathExists(index_file.fileName());
|
FS::ensureFilePathExists(index_file.fileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toml::table update;
|
||||||
|
switch (mod.provider) {
|
||||||
|
case (ModPlatform::ResourceProvider::FLAME):
|
||||||
|
if (mod.file_id.toInt() == 0 || mod.project_id.toInt() == 0) {
|
||||||
|
qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
update = toml::table{
|
||||||
|
{ "file-id", mod.file_id.toInt() },
|
||||||
|
{ "project-id", mod.project_id.toInt() },
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case (ModPlatform::ResourceProvider::MODRINTH):
|
||||||
|
if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) {
|
||||||
|
qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
update = toml::table{
|
||||||
|
{ "mod-id", mod.mod_id().toString().toStdString() },
|
||||||
|
{ "version", mod.version().toString().toStdString() },
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!index_file.open(QIODevice::ReadWrite)) {
|
if (!index_file.open(QIODevice::ReadWrite)) {
|
||||||
qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name));
|
qCritical() << QString("Could not open file %1!").arg(normalized_fname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put TOML data into the file
|
// Put TOML data into the file
|
||||||
QTextStream in_stream(&index_file);
|
QTextStream in_stream(&index_file);
|
||||||
auto addToStream = [&in_stream](QString&& key, QString value) { in_stream << QString("%1 = \"%2\"\n").arg(key, value); };
|
|
||||||
|
|
||||||
{
|
{
|
||||||
addToStream("name", mod.name);
|
auto tbl = toml::table{ { "name", mod.name.toStdString() },
|
||||||
addToStream("filename", mod.filename);
|
{ "filename", mod.filename.toStdString() },
|
||||||
addToStream("side", mod.side);
|
{ "side", sideToString(mod.side).toStdString() },
|
||||||
|
{ "download",
|
||||||
in_stream << QString("\n[download]\n");
|
toml::table{
|
||||||
addToStream("mode", mod.mode);
|
{ "mode", mod.mode.toStdString() },
|
||||||
addToStream("url", mod.url.toString());
|
{ "url", mod.url.toString().toStdString() },
|
||||||
addToStream("hash-format", mod.hash_format);
|
{ "hash-format", mod.hash_format.toStdString() },
|
||||||
addToStream("hash", mod.hash);
|
{ "hash", mod.hash.toStdString() },
|
||||||
|
} },
|
||||||
in_stream << QString("\n[update]\n");
|
{ "update", toml::table{ { ProviderCaps.name(mod.provider), update } } } };
|
||||||
in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider));
|
std::stringstream ss;
|
||||||
switch (mod.provider) {
|
ss << tbl;
|
||||||
case (ModPlatform::ResourceProvider::FLAME):
|
in_stream << QString::fromStdString(ss.str());
|
||||||
in_stream << QString("file-id = %1\n").arg(mod.file_id.toString());
|
|
||||||
in_stream << QString("project-id = %1\n").arg(mod.project_id.toString());
|
|
||||||
break;
|
|
||||||
case (ModPlatform::ResourceProvider::MODRINTH):
|
|
||||||
addToStream("mod-id", mod.mod_id().toString());
|
|
||||||
addToStream("version", mod.version().toString());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
index_file.flush();
|
index_file.flush();
|
||||||
@ -258,7 +275,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
|||||||
{ // Basic info
|
{ // Basic info
|
||||||
mod.name = stringEntry(table, "name");
|
mod.name = stringEntry(table, "name");
|
||||||
mod.filename = stringEntry(table, "filename");
|
mod.filename = stringEntry(table, "filename");
|
||||||
mod.side = stringEntry(table, "side");
|
mod.side = stringToSide(stringEntry(table, "side"));
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // [download] info
|
{ // [download] info
|
||||||
@ -313,4 +330,28 @@ auto V1::getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto V1::sideToString(Side side) -> QString
|
||||||
|
{
|
||||||
|
switch (side) {
|
||||||
|
case Side::ClientSide:
|
||||||
|
return "client";
|
||||||
|
case Side::ServerSide:
|
||||||
|
return "server";
|
||||||
|
case Side::UniversalSide:
|
||||||
|
return "both";
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto V1::stringToSide(QString side) -> Side
|
||||||
|
{
|
||||||
|
if (side == "client")
|
||||||
|
return Side::ClientSide;
|
||||||
|
if (side == "server")
|
||||||
|
return Side::ServerSide;
|
||||||
|
if (side == "both")
|
||||||
|
return Side::UniversalSide;
|
||||||
|
return Side::UniversalSide;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Packwiz
|
} // namespace Packwiz
|
||||||
|
@ -35,12 +35,12 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul
|
|||||||
|
|
||||||
class V1 {
|
class V1 {
|
||||||
public:
|
public:
|
||||||
|
enum class Side { ClientSide = 1 << 0, ServerSide = 1 << 1, UniversalSide = ClientSide | ServerSide };
|
||||||
struct Mod {
|
struct Mod {
|
||||||
QString slug{};
|
QString slug{};
|
||||||
QString name{};
|
QString name{};
|
||||||
QString filename{};
|
QString filename{};
|
||||||
// FIXME: make side an enum
|
Side side{ Side::UniversalSide };
|
||||||
QString side{ "both" };
|
|
||||||
|
|
||||||
// [download]
|
// [download]
|
||||||
QString mode{};
|
QString mode{};
|
||||||
@ -93,6 +93,9 @@ class V1 {
|
|||||||
* If the mod doesn't have a metadata, it simply returns an empty Mod object.
|
* If the mod doesn't have a metadata, it simply returns an empty Mod object.
|
||||||
* */
|
* */
|
||||||
static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod;
|
static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod;
|
||||||
|
|
||||||
|
static auto sideToString(Side side) -> QString;
|
||||||
|
static auto stringToSide(QString side) -> Side;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Packwiz
|
} // namespace Packwiz
|
||||||
|
@ -206,7 +206,7 @@ void TranslationsModel::indexReceived()
|
|||||||
reloadLocalFiles();
|
reloadLocalFiles();
|
||||||
|
|
||||||
auto language = d->m_system_locale;
|
auto language = d->m_system_locale;
|
||||||
if (!findLanguage(language)) {
|
if (!findLanguageAsOptional(language).has_value()) {
|
||||||
language = d->m_system_language;
|
language = d->m_system_language;
|
||||||
}
|
}
|
||||||
selectLanguage(language);
|
selectLanguage(language);
|
||||||
@ -417,14 +417,17 @@ int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) c
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Language* TranslationsModel::findLanguage(const QString& key)
|
QVector<Language>::Iterator TranslationsModel::findLanguage(const QString& key)
|
||||||
{
|
{
|
||||||
auto found = std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language& lang) { return lang.key == key; });
|
return std::find_if(d->m_languages.begin(), d->m_languages.end(), [&](Language& lang) { return lang.key == key; });
|
||||||
if (found == d->m_languages.end()) {
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Language> TranslationsModel::findLanguageAsOptional(const QString& key)
|
||||||
|
{
|
||||||
|
auto found = findLanguage(key);
|
||||||
|
if (found != d->m_languages.end())
|
||||||
|
return *found;
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TranslationsModel::setUseSystemLocale(bool useSystemLocale)
|
void TranslationsModel::setUseSystemLocale(bool useSystemLocale)
|
||||||
@ -436,13 +439,13 @@ void TranslationsModel::setUseSystemLocale(bool useSystemLocale)
|
|||||||
bool TranslationsModel::selectLanguage(QString key)
|
bool TranslationsModel::selectLanguage(QString key)
|
||||||
{
|
{
|
||||||
QString& langCode = key;
|
QString& langCode = key;
|
||||||
auto langPtr = findLanguage(key);
|
auto langPtr = findLanguageAsOptional(key);
|
||||||
|
|
||||||
if (langCode.isEmpty()) {
|
if (langCode.isEmpty()) {
|
||||||
d->no_language_set = true;
|
d->no_language_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!langPtr) {
|
if (!langPtr.has_value()) {
|
||||||
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
|
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
|
||||||
langCode = defaultLangCode;
|
langCode = defaultLangCode;
|
||||||
} else {
|
} else {
|
||||||
@ -527,9 +530,8 @@ bool TranslationsModel::selectLanguage(QString key)
|
|||||||
QModelIndex TranslationsModel::selectedIndex()
|
QModelIndex TranslationsModel::selectedIndex()
|
||||||
{
|
{
|
||||||
auto found = findLanguage(d->m_selectedLanguage);
|
auto found = findLanguage(d->m_selectedLanguage);
|
||||||
if (found) {
|
if (found != d->m_languages.end()) {
|
||||||
// QVector iterator freely converts to pointer to contained type
|
return index(std::distance(d->m_languages.begin(), found), 0, QModelIndex());
|
||||||
return index(found - d->m_languages.begin(), 0, QModelIndex());
|
|
||||||
}
|
}
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
@ -562,8 +564,8 @@ void TranslationsModel::updateLanguage(QString key)
|
|||||||
qWarning() << "Cannot update builtin language" << key;
|
qWarning() << "Cannot update builtin language" << key;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto found = findLanguage(key);
|
auto found = findLanguageAsOptional(key);
|
||||||
if (!found) {
|
if (!found.has_value()) {
|
||||||
qWarning() << "Cannot update invalid language" << key;
|
qWarning() << "Cannot update invalid language" << key;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -578,8 +580,8 @@ void TranslationsModel::downloadTranslation(QString key)
|
|||||||
d->m_nextDownload = key;
|
d->m_nextDownload = key;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto lang = findLanguage(key);
|
auto lang = findLanguageAsOptional(key);
|
||||||
if (!lang) {
|
if (!lang.has_value()) {
|
||||||
qWarning() << "Will not download an unknown translation" << key;
|
qWarning() << "Will not download an unknown translation" << key;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
struct Language;
|
struct Language;
|
||||||
|
|
||||||
@ -40,7 +41,8 @@ class TranslationsModel : public QAbstractListModel {
|
|||||||
void setUseSystemLocale(bool useSystemLocale);
|
void setUseSystemLocale(bool useSystemLocale);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Language* findLanguage(const QString& key);
|
QVector<Language>::Iterator findLanguage(const QString& key);
|
||||||
|
std::optional<Language> findLanguageAsOptional(const QString& key);
|
||||||
void reloadLocalFiles();
|
void reloadLocalFiles();
|
||||||
void downloadTranslation(QString key);
|
void downloadTranslation(QString key);
|
||||||
void downloadNext();
|
void downloadNext();
|
||||||
|
@ -237,8 +237,7 @@ void NewInstanceDialog::setSuggestedIcon(const QString& key)
|
|||||||
|
|
||||||
InstanceTask* NewInstanceDialog::extractTask()
|
InstanceTask* NewInstanceDialog::extractTask()
|
||||||
{
|
{
|
||||||
InstanceTask* extracted = creationTask.get();
|
InstanceTask* extracted = creationTask.release();
|
||||||
creationTask.release();
|
|
||||||
|
|
||||||
InstanceName inst_name(ui->instNameTextBox->placeholderText().trimmed(), importVersion);
|
InstanceName inst_name(ui->instNameTextBox->placeholderText().trimmed(), importVersion);
|
||||||
inst_name.setName(ui->instNameTextBox->text().trimmed());
|
inst_name.setName(ui->instNameTextBox->text().trimmed());
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
#include "ui_ManagedPackPage.h"
|
#include "ui_ManagedPackPage.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QProxyStyle>
|
#include <QProxyStyle>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
@ -223,6 +224,7 @@ ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWin
|
|||||||
Q_ASSERT(inst->isManagedPack());
|
Q_ASSERT(inst->isManagedPack());
|
||||||
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
||||||
connect(ui->updateButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::update);
|
connect(ui->updateButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::update);
|
||||||
|
connect(ui->updateFromFileButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::updateFromFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MODRINTH
|
// MODRINTH
|
||||||
@ -350,6 +352,27 @@ void ModrinthManagedPackPage::update()
|
|||||||
m_instance_window->close();
|
m_instance_window->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModrinthManagedPackPage::updateFromFile()
|
||||||
|
{
|
||||||
|
auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "Modrinth pack (*.mrpack *.zip)");
|
||||||
|
QMap<QString, QString> extra_info;
|
||||||
|
extra_info.insert("pack_id", m_inst->getManagedPackID());
|
||||||
|
extra_info.insert("pack_version_id", QString());
|
||||||
|
extra_info.insert("original_instance_id", m_inst->id());
|
||||||
|
|
||||||
|
auto extracted = new InstanceImportTask(output, this, std::move(extra_info));
|
||||||
|
|
||||||
|
extracted->setName(m_inst->name());
|
||||||
|
extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
|
||||||
|
extracted->setIcon(m_inst->iconKey());
|
||||||
|
extracted->setConfirmUpdate(false);
|
||||||
|
|
||||||
|
auto did_succeed = runUpdateTask(extracted);
|
||||||
|
|
||||||
|
if (m_instance_window && did_succeed)
|
||||||
|
m_instance_window->close();
|
||||||
|
}
|
||||||
|
|
||||||
// FLAME
|
// FLAME
|
||||||
|
|
||||||
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
||||||
@ -358,6 +381,7 @@ FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* i
|
|||||||
Q_ASSERT(inst->isManagedPack());
|
Q_ASSERT(inst->isManagedPack());
|
||||||
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
||||||
connect(ui->updateButton, &QPushButton::clicked, this, &FlameManagedPackPage::update);
|
connect(ui->updateButton, &QPushButton::clicked, this, &FlameManagedPackPage::update);
|
||||||
|
connect(ui->updateFromFileButton, &QPushButton::clicked, this, &FlameManagedPackPage::updateFromFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlameManagedPackPage::parseManagedPack()
|
void FlameManagedPackPage::parseManagedPack()
|
||||||
@ -492,4 +516,25 @@ void FlameManagedPackPage::update()
|
|||||||
m_instance_window->close();
|
m_instance_window->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlameManagedPackPage::updateFromFile()
|
||||||
|
{
|
||||||
|
auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "CurseForge pack (*.zip)");
|
||||||
|
|
||||||
|
QMap<QString, QString> extra_info;
|
||||||
|
extra_info.insert("pack_id", m_inst->getManagedPackID());
|
||||||
|
extra_info.insert("pack_version_id", QString());
|
||||||
|
extra_info.insert("original_instance_id", m_inst->id());
|
||||||
|
|
||||||
|
auto extracted = new InstanceImportTask(output, this, std::move(extra_info));
|
||||||
|
|
||||||
|
extracted->setName(m_inst->name());
|
||||||
|
extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
|
||||||
|
extracted->setIcon(m_inst->iconKey());
|
||||||
|
extracted->setConfirmUpdate(false);
|
||||||
|
|
||||||
|
auto did_succeed = runUpdateTask(extracted);
|
||||||
|
|
||||||
|
if (m_instance_window && did_succeed)
|
||||||
|
m_instance_window->close();
|
||||||
|
}
|
||||||
#include "ManagedPackPage.moc"
|
#include "ManagedPackPage.moc"
|
||||||
|
@ -65,6 +65,7 @@ class ManagedPackPage : public QWidget, public BasePage {
|
|||||||
virtual void suggestVersion();
|
virtual void suggestVersion();
|
||||||
|
|
||||||
virtual void update(){};
|
virtual void update(){};
|
||||||
|
virtual void updateFromFile(){};
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
/** Does the necessary UI changes for when something failed.
|
/** Does the necessary UI changes for when something failed.
|
||||||
@ -123,6 +124,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage {
|
|||||||
void suggestVersion() override;
|
void suggestVersion() override;
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
|
void updateFromFile() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetJob::Ptr m_fetch_job = nullptr;
|
NetJob::Ptr m_fetch_job = nullptr;
|
||||||
@ -145,6 +147,7 @@ class FlameManagedPackPage final : public ManagedPackPage {
|
|||||||
void suggestVersion() override;
|
void suggestVersion() override;
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
|
void updateFromFile() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetJob::Ptr m_fetch_job = nullptr;
|
NetJob::Ptr m_fetch_job = nullptr;
|
||||||
|
@ -153,6 +153,19 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="updateFromFileButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Update from file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
63
launcher/ui/pages/modplatform/OptionalModDialog.cpp
Normal file
63
launcher/ui/pages/modplatform/OptionalModDialog.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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 "OptionalModDialog.h"
|
||||||
|
#include "ui_OptionalModDialog.h"
|
||||||
|
|
||||||
|
OptionalModDialog::OptionalModDialog(QWidget* parent, const QStringList& mods) : QDialog(parent), ui(new Ui::OptionalModDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
for (const QString& mod : mods) {
|
||||||
|
auto item = new QListWidgetItem(mod, ui->list);
|
||||||
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||||
|
item->setCheckState(Qt::Unchecked);
|
||||||
|
item->setData(Qt::UserRole, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ui->selectAllButton, &QPushButton::clicked, ui->list, [this] {
|
||||||
|
for (int i = 0; i < ui->list->count(); i++)
|
||||||
|
ui->list->item(i)->setCheckState(Qt::Checked);
|
||||||
|
});
|
||||||
|
connect(ui->clearAllButton, &QPushButton::clicked, ui->list, [this] {
|
||||||
|
for (int i = 0; i < ui->list->count(); i++)
|
||||||
|
ui->list->item(i)->setCheckState(Qt::Unchecked);
|
||||||
|
});
|
||||||
|
connect(ui->list, &QListWidget::itemActivated, [](QListWidgetItem* item) {
|
||||||
|
if (item->checkState() == Qt::Checked)
|
||||||
|
item->setCheckState(Qt::Unchecked);
|
||||||
|
else
|
||||||
|
item->setCheckState(Qt::Checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalModDialog::~OptionalModDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList OptionalModDialog::getResult()
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
result.reserve(ui->list->count());
|
||||||
|
for (int i = 0; i < ui->list->count(); i++) {
|
||||||
|
auto item = ui->list->item(i);
|
||||||
|
if (item->checkState() == Qt::Checked)
|
||||||
|
result.append(item->data(Qt::UserRole).toString());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
39
launcher/ui/pages/modplatform/OptionalModDialog.h
Normal file
39
launcher/ui/pages/modplatform/OptionalModDialog.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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 <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class OptionalModDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionalModDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
OptionalModDialog(QWidget* parent, const QStringList& mods);
|
||||||
|
~OptionalModDialog() override;
|
||||||
|
|
||||||
|
QStringList getResult();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::OptionalModDialog* ui;
|
||||||
|
};
|
113
launcher/ui/pages/modplatform/OptionalModDialog.ui
Normal file
113
launcher/ui/pages/modplatform/OptionalModDialog.ui
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>OptionalModDialog</class>
|
||||||
|
<widget class="QDialog" name="OptionalModDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>550</width>
|
||||||
|
<height>310</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Select Optional Mods</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="list">
|
||||||
|
<property name="defaultDropAction">
|
||||||
|
<enum>Qt::IgnoreAction</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="selectAllButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select All</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="clearAllButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Deselect All</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Unchecked mods will be disabled.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>OptionalModDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>274</x>
|
||||||
|
<y>284</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>274</x>
|
||||||
|
<y>154</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>OptionalModDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>274</x>
|
||||||
|
<y>284</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>274</x>
|
||||||
|
<y>154</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -63,7 +63,7 @@ QVariant ListModel::data(const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder");
|
auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder");
|
||||||
|
|
||||||
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower());
|
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(pack.safeName);
|
||||||
((ListModel*)this)->requestLogo(pack.safeName, url);
|
((ListModel*)this)->requestLogo(pack.safeName, url);
|
||||||
|
|
||||||
return icon;
|
return icon;
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "modplatform/atlauncher/ATLPackIndex.h"
|
#include "modplatform/atlauncher/ATLPackManifest.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -114,8 +114,8 @@ void AtlPage::suggestCurrent()
|
|||||||
auto uiSupport = new AtlUserInteractionSupportImpl(this);
|
auto uiSupport = new AtlUserInteractionSupportImpl(this);
|
||||||
dialog->setSuggestedPack(selected.name, selectedVersion, new ATLauncher::PackInstallTask(uiSupport, selected.name, selectedVersion));
|
dialog->setSuggestedPack(selected.name, selectedVersion, new ATLauncher::PackInstallTask(uiSupport, selected.name, selectedVersion));
|
||||||
|
|
||||||
auto editedLogoName = selected.safeName;
|
auto editedLogoName = "atl_" + selected.safeName;
|
||||||
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(selected.safeName.toLower());
|
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1").arg(selected.safeName);
|
||||||
listModel->getLogo(selected.safeName, url,
|
listModel->getLogo(selected.safeName, url,
|
||||||
[this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
[this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
||||||
}
|
}
|
||||||
|
@ -228,8 +228,7 @@ void FlamePage::suggestCurrent()
|
|||||||
extra_info.insert("pack_version_id", QString::number(version.fileId));
|
extra_info.insert("pack_version_id", QString::number(version.fileId));
|
||||||
|
|
||||||
dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info)));
|
dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info)));
|
||||||
QString editedLogoName;
|
QString editedLogoName = "curseforge_" + current.logoName;
|
||||||
editedLogoName = "curseforge_" + current.logoName;
|
|
||||||
listModel->getLogo(current.logoName, current.logoUrl,
|
listModel->getLogo(current.logoName, current.logoUrl,
|
||||||
[this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
[this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ void ImportFTBPage::suggestCurrent()
|
|||||||
}
|
}
|
||||||
|
|
||||||
dialog->setSuggestedPack(selected.name, new PackInstallTask(selected));
|
dialog->setSuggestedPack(selected.name, new PackInstallTask(selected));
|
||||||
QString editedLogoName = QString("ftb_%1").arg(selected.id);
|
QString editedLogoName = QString("ftb_%1_%2,jpg").arg(selected.name, selected.id);
|
||||||
dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName);
|
dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,15 +179,11 @@ void Page::suggestCurrent()
|
|||||||
}
|
}
|
||||||
|
|
||||||
dialog->setSuggestedPack(selected.name, selectedVersion, new PackInstallTask(APPLICATION->network(), selected, selectedVersion));
|
dialog->setSuggestedPack(selected.name, selectedVersion, new PackInstallTask(APPLICATION->network(), selected, selectedVersion));
|
||||||
QString editedLogoName;
|
QString editedLogoName = selected.logo;
|
||||||
if (selected.logo.toLower().startsWith("ftb")) {
|
if (!selected.logo.toLower().startsWith("ftb")) {
|
||||||
editedLogoName = selected.logo;
|
editedLogoName = "ftb_" + editedLogoName;
|
||||||
} else {
|
|
||||||
editedLogoName = "ftb_" + selected.logo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editedLogoName = editedLogoName.left(editedLogoName.lastIndexOf(".png"));
|
|
||||||
|
|
||||||
if (selected.type == PackType::Public) {
|
if (selected.type == PackType::Public) {
|
||||||
publicListModel->getLogo(selected.logo,
|
publicListModel->getLogo(selected.logo,
|
||||||
[this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
[this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo, editedLogoName); });
|
||||||
|
@ -41,7 +41,9 @@
|
|||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
#include "ui/widgets/ProjectItem.h"
|
#include "ui/widgets/ProjectItem.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
Technic::ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {}
|
Technic::ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {}
|
||||||
|
|
||||||
@ -193,7 +195,7 @@ void Technic::ListModel::searchRequestFinished()
|
|||||||
pack.logoName = "null";
|
pack.logoName = "null";
|
||||||
} else {
|
} else {
|
||||||
pack.logoUrl = rawURL;
|
pack.logoUrl = rawURL;
|
||||||
pack.logoName = rawURL.section(QLatin1Char('/'), -1);
|
pack.logoName = pack.slug + "." + QFileInfo(QUrl(rawURL).fileName()).suffix();
|
||||||
}
|
}
|
||||||
pack.broken = false;
|
pack.broken = false;
|
||||||
newList.append(pack);
|
newList.append(pack);
|
||||||
@ -215,7 +217,7 @@ void Technic::ListModel::searchRequestFinished()
|
|||||||
auto iconUrl = Json::requireString(iconObj, "url");
|
auto iconUrl = Json::requireString(iconObj, "url");
|
||||||
|
|
||||||
pack.logoUrl = iconUrl;
|
pack.logoUrl = iconUrl;
|
||||||
pack.logoName = iconUrl.section(QLatin1Char('/'), -1);
|
pack.logoName = pack.slug + "." + QFileInfo(QUrl(iconUrl).fileName()).suffix();
|
||||||
} else {
|
} else {
|
||||||
pack.logoUrl = "null";
|
pack.logoUrl = "null";
|
||||||
pack.logoName = "null";
|
pack.logoName = "null";
|
||||||
|
@ -42,7 +42,7 @@ class PackwizTest : public QObject {
|
|||||||
|
|
||||||
QCOMPARE(metadata.name, "Borderless Mining");
|
QCOMPARE(metadata.name, "Borderless Mining");
|
||||||
QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar");
|
QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar");
|
||||||
QCOMPARE(metadata.side, "client");
|
QCOMPARE(metadata.side, Packwiz::V1::Side::ClientSide);
|
||||||
|
|
||||||
QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar"));
|
QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar"));
|
||||||
QCOMPARE(metadata.hash_format, "sha512");
|
QCOMPARE(metadata.hash_format, "sha512");
|
||||||
@ -72,7 +72,7 @@ class PackwizTest : public QObject {
|
|||||||
|
|
||||||
QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)");
|
QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)");
|
||||||
QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar");
|
QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar");
|
||||||
QCOMPARE(metadata.side, "both");
|
QCOMPARE(metadata.side, Packwiz::V1::Side::UniversalSide);
|
||||||
|
|
||||||
QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar"));
|
QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar"));
|
||||||
QCOMPARE(metadata.hash_format, "murmur2");
|
QCOMPARE(metadata.hash_format, "murmur2");
|
||||||
|
@ -55,6 +55,8 @@ class VersionTest : public QObject {
|
|||||||
<< "2.2.0" << true << false;
|
<< "2.2.0" << true << false;
|
||||||
QTest::newRow("lessThan, two-digit") << "1.41"
|
QTest::newRow("lessThan, two-digit") << "1.41"
|
||||||
<< "1.42" << true << false;
|
<< "1.42" << true << false;
|
||||||
|
QTest::newRow("lessThan, snapshot") << "1.20.0-rc2"
|
||||||
|
<< "1.20.1" << true << false;
|
||||||
|
|
||||||
QTest::newRow("greaterThan, explicit 1") << "1.2.1"
|
QTest::newRow("greaterThan, explicit 1") << "1.2.1"
|
||||||
<< "1.2.0" << false << false;
|
<< "1.2.0" << false << false;
|
||||||
@ -72,6 +74,8 @@ class VersionTest : public QObject {
|
|||||||
<< "1.2" << false << false;
|
<< "1.2" << false << false;
|
||||||
QTest::newRow("greaterThan, two-digit") << "1.42"
|
QTest::newRow("greaterThan, two-digit") << "1.42"
|
||||||
<< "1.41" << false << false;
|
<< "1.41" << false << false;
|
||||||
|
QTest::newRow("greaterThan, snapshot") << "1.20.2-rc2"
|
||||||
|
<< "1.20.1" << false << false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
Loading…
Reference in New Issue
Block a user