Merge branch 'release-5.x' of https://github.com/PrismLauncher/PrismLauncher into prism
15
.github/workflows/build.yml
vendored
@ -30,12 +30,12 @@ jobs:
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-Legacy"
|
||||
msystem: mingw32
|
||||
msystem: clang32
|
||||
qt_ver: 5
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows"
|
||||
msystem: mingw32
|
||||
msystem: clang64
|
||||
qt_ver: 6
|
||||
|
||||
- os: macos-12
|
||||
@ -89,6 +89,7 @@ jobs:
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
mingw-w64-x86_64-binutils
|
||||
pacboy: >-
|
||||
toolchain:p
|
||||
cmake:p
|
||||
@ -99,12 +100,11 @@ jobs:
|
||||
qt${{ matrix.qt_ver }}-imageformats:p
|
||||
quazip-qt${{ matrix.qt_ver }}:p
|
||||
ccache:p
|
||||
nsis:p
|
||||
${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }}
|
||||
|
||||
- name: Setup ccache
|
||||
if: runner.os != 'Windows' && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.1
|
||||
uses: hendrikmuhs/ccache-action@v1.2.3
|
||||
with:
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
||||
|
||||
@ -126,7 +126,7 @@ jobs:
|
||||
|
||||
- name: Retrieve ccache cache (Windows)
|
||||
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
|
||||
uses: actions/cache@v3.0.2
|
||||
uses: actions/cache@v3.0.11
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
||||
@ -194,7 +194,7 @@ jobs:
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
|
||||
|
||||
- name: Configure CMake (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
@ -280,7 +280,7 @@ jobs:
|
||||
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
if [ "${{ matrix.qt_ver }}" == "5" ]; then
|
||||
cp /mingw32/bin/libcrypto-1_1.dll /mingw32/bin/libssl-1_1.dll ./
|
||||
cp /clang32/bin/libcrypto-1_1.dll /clang32/bin/libssl-1_1.dll ./
|
||||
fi
|
||||
|
||||
- name: Package (Windows, portable)
|
||||
@ -292,7 +292,6 @@ jobs:
|
||||
|
||||
- name: Package (Windows, installer)
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||
|
@ -71,13 +71,13 @@ endif()
|
||||
##################################### Set Application options #####################################
|
||||
|
||||
######## Set URLs ########
|
||||
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch PrismLauncher's news RSS feed from.")
|
||||
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
||||
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||
|
||||
######## Set version numbers ########
|
||||
set(Launcher_VERSION_MAJOR 5)
|
||||
set(Launcher_VERSION_MINOR 0)
|
||||
set(Launcher_VERSION_MINOR 1)
|
||||
|
||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||
@ -221,14 +221,14 @@ if(UNIX AND APPLE)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||
|
||||
# Mac bundle settings
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}")
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}")
|
||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=")
|
||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml")
|
||||
|
||||
@ -248,7 +248,7 @@ if(UNIX AND APPLE)
|
||||
elseif(UNIX)
|
||||
set(BINARY_DEST_DIR "bin")
|
||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||
set(JARS_DEST_DIR "share/jars")
|
||||
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
||||
set(LAUNCHER_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
||||
set(LAUNCHER_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
|
||||
set(LAUNCHER_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
|
||||
|
@ -42,6 +42,7 @@ Config::Config()
|
||||
{
|
||||
// Name and copyright
|
||||
LAUNCHER_NAME = "@Launcher_Name@";
|
||||
LAUNCHER_APP_BINARY_NAME = "@Launcher_APP_BINARY_NAME@";
|
||||
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
||||
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||
|
@ -44,6 +44,7 @@ class Config {
|
||||
public:
|
||||
Config();
|
||||
QString LAUNCHER_NAME;
|
||||
QString LAUNCHER_APP_BINARY_NAME;
|
||||
QString LAUNCHER_DISPLAYNAME;
|
||||
QString LAUNCHER_COPYRIGHT;
|
||||
QString LAUNCHER_DOMAIN;
|
||||
|
@ -62,6 +62,7 @@
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "ui/WinDarkmode.h"
|
||||
#include <versionhelpers.h>
|
||||
#endif
|
||||
|
||||
#include "ui/setupwizard/SetupWizard.h"
|
||||
@ -1127,15 +1128,6 @@ std::vector<ITheme *> Application::getValidApplicationThemes()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Application::isFlatpak()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QFile::exists("/.flatpak-info");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::setApplicationTheme(const QString& name, bool initial)
|
||||
{
|
||||
auto systemPalette = qApp->palette();
|
||||
@ -1145,7 +1137,7 @@ void Application::setApplicationTheme(const QString& name, bool initial)
|
||||
auto & theme = (*themeIter).second;
|
||||
theme->apply(initial);
|
||||
#ifdef Q_OS_WIN
|
||||
if (m_mainWindow) {
|
||||
if (m_mainWindow && IsWindows10OrGreater()) {
|
||||
if (QString::compare(theme->id(), "dark") == 0) {
|
||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
|
||||
} else {
|
||||
@ -1386,10 +1378,13 @@ MainWindow* Application::showMainWindow(bool minimized)
|
||||
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
|
||||
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
|
||||
#ifdef Q_OS_WIN
|
||||
if (QString::compare(settings()->get("ApplicationTheme").toString(), "dark") == 0) {
|
||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
|
||||
} else {
|
||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
|
||||
if (IsWindows10OrGreater())
|
||||
{
|
||||
if (QString::compare(settings()->get("ApplicationTheme").toString(), "dark") == 0) {
|
||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
|
||||
} else {
|
||||
WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(minimized)
|
||||
@ -1574,7 +1569,7 @@ QString Application::getJarPath(QString jarFile)
|
||||
{
|
||||
QStringList potentialPaths = {
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
FS::PathCombine(m_rootPath, "share/jars"),
|
||||
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
|
||||
#endif
|
||||
FS::PathCombine(m_rootPath, "jars"),
|
||||
FS::PathCombine(applicationDirPath(), "jars")
|
||||
|
@ -116,8 +116,6 @@ public:
|
||||
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
bool isFlatpak();
|
||||
|
||||
void setIconTheme(const QString& name);
|
||||
|
||||
std::vector<ITheme *> getValidApplicationThemes();
|
||||
|
@ -119,7 +119,7 @@ bool openDirectory(const QString &path, bool ensureExists)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -140,7 +140,7 @@ bool openFile(const QString &path)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -158,7 +158,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
|
||||
qDebug() << "Opening file" << path << "using" << application;
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
@ -178,7 +178,7 @@ bool run(const QString &application, const QStringList &args, const QString &wor
|
||||
{
|
||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
return IndirectOpen([&]()
|
||||
@ -203,7 +203,7 @@ bool openUrl(const QUrl &url)
|
||||
return QDesktopServices::openUrl(url);
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -216,4 +216,13 @@ bool openUrl(const QUrl &url)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isFlatpak()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QFile::exists("/.flatpak-info");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,4 +33,6 @@ namespace DesktopServices
|
||||
* Open the URL, most likely in a browser. Maybe.
|
||||
*/
|
||||
bool openUrl(const QUrl &url);
|
||||
|
||||
bool isFlatpak();
|
||||
}
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QUrl>
|
||||
#include "DesktopServices.h"
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include <objbase.h>
|
||||
@ -182,6 +183,21 @@ bool copy::operator()(const QString& offset)
|
||||
if (!m_followSymlinks)
|
||||
opt |= copy_opts::copy_symlinks;
|
||||
|
||||
// Function that'll do the actual copying
|
||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
||||
if (m_blacklist && m_blacklist->matches(relative_dst_path))
|
||||
return;
|
||||
|
||||
auto dst_path = PathCombine(dst, relative_dst_path);
|
||||
ensureFilePathExists(dst_path);
|
||||
|
||||
fs::copy(toStdString(src_path), toStdString(dst_path), opt, err);
|
||||
if (err) {
|
||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << src_path;
|
||||
qDebug() << "Destination file:" << dst_path;
|
||||
}
|
||||
};
|
||||
|
||||
// We can't use copy_opts::recursive because we need to take into account the
|
||||
// blacklisted paths, so we iterate over the source directory, and if there's no blacklist
|
||||
@ -193,20 +209,13 @@ bool copy::operator()(const QString& offset)
|
||||
auto src_path = source_it.next();
|
||||
auto relative_path = src_dir.relativeFilePath(src_path);
|
||||
|
||||
if (m_blacklist && m_blacklist->matches(relative_path))
|
||||
continue;
|
||||
|
||||
auto dst_path = PathCombine(dst, relative_path);
|
||||
ensureFilePathExists(dst_path);
|
||||
|
||||
fs::copy(toStdString(src_path), toStdString(dst_path), opt, err);
|
||||
if (err) {
|
||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << src_path;
|
||||
qDebug() << "Destination file:" << dst_path;
|
||||
}
|
||||
copy_file(src_path, relative_path);
|
||||
}
|
||||
|
||||
// If the root src is not a directory, the previous iterator won't run.
|
||||
if (!fs::is_directory(toStdString(src)))
|
||||
copy_file(src, "");
|
||||
|
||||
return err.value() == 0;
|
||||
}
|
||||
|
||||
@ -228,6 +237,9 @@ bool trash(QString path, QString *pathInTrash = nullptr)
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
return false;
|
||||
#else
|
||||
// FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal
|
||||
if (DesktopServices::isFlatpak())
|
||||
return false;
|
||||
return QFile::moveToTrash(path, pathInTrash);
|
||||
#endif
|
||||
}
|
||||
@ -401,6 +413,7 @@ bool overrideFolder(QString overwritten_path, QString override_path)
|
||||
std::error_code err;
|
||||
fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing;
|
||||
|
||||
// FIXME: hello traveller! Apparently std::copy does NOT overwrite existing files on GNU libstdc++ on Windows?
|
||||
fs::copy(toStdString(override_path), toStdString(overwritten_path), opt, err);
|
||||
|
||||
if (err) {
|
||||
|
@ -72,7 +72,7 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
|
||||
uncompLength *= 2;
|
||||
}
|
||||
|
||||
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
||||
strm.next_out = reinterpret_cast<Bytef *>((uncompressedBytes.data() + strm.total_out));
|
||||
strm.avail_out = uncompLength - strm.total_out;
|
||||
|
||||
// Inflate another chunk.
|
||||
@ -129,7 +129,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
{
|
||||
compressedBytes.resize(compressedBytes.size() * 2);
|
||||
}
|
||||
zs.next_out = (Bytef *) (compressedBytes.data() + offset);
|
||||
zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset));
|
||||
temp = zs.avail_out = compressedBytes.size() - offset;
|
||||
ret = deflate(&zs, Z_FINISH);
|
||||
offset += temp - zs.avail_out;
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
}
|
||||
void put(QByteArray input)
|
||||
{
|
||||
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
|
||||
hoedown_buffer_put(buf, reinterpret_cast<uint8_t *>(input.data()), input.size());
|
||||
}
|
||||
const uint8_t * data() const
|
||||
{
|
||||
|
@ -379,7 +379,9 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
}
|
||||
|
||||
return addJavasFromEnv(candidates);
|
||||
candidates = addJavasFromEnv(candidates);
|
||||
candidates.removeDuplicates();
|
||||
return candidates;
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_MAC)
|
||||
@ -402,7 +404,9 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
|
||||
javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
|
||||
}
|
||||
return addJavasFromEnv(javas);
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_LINUX)
|
||||
@ -448,7 +452,9 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
scanJavaDir("/opt/jdks");
|
||||
// flatpak
|
||||
scanJavaDir("/app/jdk");
|
||||
return addJavasFromEnv(javas);
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
}
|
||||
#else
|
||||
QList<QString> JavaUtils::FindJavaPaths()
|
||||
|
@ -736,7 +736,12 @@ void PackInstallTask::downloadMods()
|
||||
QVector<QString> selectedMods;
|
||||
if (!optionalMods.isEmpty()) {
|
||||
setStatus(tr("Selecting optional mods..."));
|
||||
selectedMods = m_support->chooseOptionalMods(m_version, optionalMods);
|
||||
auto mods = m_support->chooseOptionalMods(m_version, optionalMods);
|
||||
if (!mods.has_value()) {
|
||||
emitAborted();
|
||||
return;
|
||||
}
|
||||
selectedMods = mods.value();
|
||||
}
|
||||
|
||||
setStatus(tr("Downloading mods..."));
|
||||
|
@ -62,7 +62,7 @@ public:
|
||||
/**
|
||||
* Requests a user interaction to select which optional mods should be installed.
|
||||
*/
|
||||
virtual QVector<QString> chooseOptionalMods(PackVersion version, QVector<ATLauncher::VersionMod> mods) = 0;
|
||||
virtual std::optional<QVector<QString>> chooseOptionalMods(PackVersion version, QVector<ATLauncher::VersionMod> mods) = 0;
|
||||
|
||||
/**
|
||||
* Requests a user interaction to select a component version from a given version list
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "Json.h"
|
||||
#include "net/Upload.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||
|
||||
Flame::FileResolvingTask::FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess)
|
||||
: m_network(network), m_toProcess(toProcess)
|
||||
{}
|
||||
@ -12,6 +14,8 @@ bool Flame::FileResolvingTask::abort()
|
||||
bool aborted = true;
|
||||
if (m_dljob)
|
||||
aborted &= m_dljob->abort();
|
||||
if (m_checkJob)
|
||||
aborted &= m_checkJob->abort();
|
||||
return aborted ? Task::abort() : false;
|
||||
}
|
||||
|
||||
@ -40,7 +44,7 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
setProgress(1, 3);
|
||||
int index = 0;
|
||||
// job to check modrinth for blocked projects
|
||||
auto job = new NetJob("Modrinth check", m_network);
|
||||
m_checkJob = new NetJob("Modrinth check", m_network);
|
||||
blockedProjects = QMap<File *,QByteArray *>();
|
||||
auto doc = Json::requireDocument(*result);
|
||||
auto array = Json::requireArray(doc.object()["data"]);
|
||||
@ -60,15 +64,15 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
out.resolved = true;
|
||||
});
|
||||
|
||||
job->addNetAction(dl);
|
||||
m_checkJob->addNetAction(dl);
|
||||
blockedProjects.insert(&out, output);
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
connect(job, &NetJob::finished, this, &Flame::FileResolvingTask::modrinthCheckFinished);
|
||||
connect(m_checkJob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::modrinthCheckFinished);
|
||||
|
||||
job->start();
|
||||
m_checkJob->start();
|
||||
}
|
||||
|
||||
void Flame::FileResolvingTask::modrinthCheckFinished() {
|
||||
@ -82,18 +86,21 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
|
||||
delete bytes;
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*bytes);
|
||||
auto obj = doc.object();
|
||||
auto array = Json::requireArray(obj,"files");
|
||||
for (auto file: array) {
|
||||
auto fileObj = Json::requireObject(file);
|
||||
auto primary = Json::requireBoolean(fileObj,"primary");
|
||||
if (primary) {
|
||||
out->url = Json::requireUrl(fileObj,"url");
|
||||
qDebug() << "Found alternative on modrinth " << out->fileName;
|
||||
break;
|
||||
}
|
||||
auto file = Modrinth::loadIndexedPackVersion(obj);
|
||||
|
||||
// If there's more than one mod loader for this version, we can't know for sure
|
||||
// which file is relative to each loader, so it's best to not use any one and
|
||||
// let the user download it manually.
|
||||
if (file.loaders.size() <= 1) {
|
||||
out->url = file.downloadUrl;
|
||||
qDebug() << "Found alternative on modrinth " << out->fileName;
|
||||
} else {
|
||||
out->resolved = false;
|
||||
}
|
||||
|
||||
delete bytes;
|
||||
}
|
||||
//copy to an output list and filter out projects found on modrinth
|
||||
|
@ -30,8 +30,9 @@ protected slots:
|
||||
private: /* data */
|
||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||
Flame::Manifest m_toProcess;
|
||||
std::shared_ptr<QByteArray> result;
|
||||
std::shared_ptr<QByteArray> result;
|
||||
NetJob::Ptr m_dljob;
|
||||
NetJob::Ptr m_checkJob;
|
||||
|
||||
void modrinthCheckFinished();
|
||||
|
||||
|
@ -15,6 +15,7 @@ void NetworkModAPI::searchMods(CallerType* caller, SearchArgs&& args) const
|
||||
|
||||
QObject::connect(netJob, &NetJob::started, caller, [caller, netJob] { caller->setActiveJob(netJob); });
|
||||
QObject::connect(netJob, &NetJob::failed, caller, &CallerType::searchRequestFailed);
|
||||
QObject::connect(netJob, &NetJob::aborted, caller, &CallerType::searchRequestAborted);
|
||||
QObject::connect(netJob, &NetJob::succeeded, caller, [caller, response] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
|
@ -1,20 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@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/>.
|
||||
*/
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@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 "ModrinthPackIndex.h"
|
||||
#include "ModrinthAPI.h"
|
||||
@ -35,7 +35,7 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
|
||||
pack.provider = ModPlatform::Provider::MODRINTH;
|
||||
pack.name = Json::requireString(obj, "title");
|
||||
|
||||
|
||||
pack.slug = Json::ensureString(obj, "slug", "");
|
||||
if (!pack.slug.isEmpty())
|
||||
pack.websiteUrl = "https://modrinth.com/mod/" + pack.slug;
|
||||
@ -59,23 +59,23 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.extraData.issuesUrl = Json::ensureString(obj, "issues_url");
|
||||
if(pack.extraData.issuesUrl.endsWith('/'))
|
||||
if (pack.extraData.issuesUrl.endsWith('/'))
|
||||
pack.extraData.issuesUrl.chop(1);
|
||||
|
||||
pack.extraData.sourceUrl = Json::ensureString(obj, "source_url");
|
||||
if(pack.extraData.sourceUrl.endsWith('/'))
|
||||
if (pack.extraData.sourceUrl.endsWith('/'))
|
||||
pack.extraData.sourceUrl.chop(1);
|
||||
|
||||
pack.extraData.wikiUrl = Json::ensureString(obj, "wiki_url");
|
||||
if(pack.extraData.wikiUrl.endsWith('/'))
|
||||
if (pack.extraData.wikiUrl.endsWith('/'))
|
||||
pack.extraData.wikiUrl.chop(1);
|
||||
|
||||
pack.extraData.discordUrl = Json::ensureString(obj, "discord_url");
|
||||
if(pack.extraData.discordUrl.endsWith('/'))
|
||||
if (pack.extraData.discordUrl.endsWith('/'))
|
||||
pack.extraData.discordUrl.chop(1);
|
||||
|
||||
auto donate_arr = Json::ensureArray(obj, "donation_urls");
|
||||
for(auto d : donate_arr){
|
||||
for (auto d : donate_arr) {
|
||||
auto d_obj = Json::requireObject(d);
|
||||
|
||||
ModPlatform::DonationData donate;
|
||||
@ -104,7 +104,7 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
auto obj = versionIter.toObject();
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
|
||||
if(file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
@ -116,7 +116,8 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
pack.versionsLoaded = true;
|
||||
}
|
||||
|
||||
auto Modrinth::loadIndexedPackVersion(QJsonObject &obj, QString preferred_hash_type, QString preferred_file_name) -> ModPlatform::IndexedVersion
|
||||
auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_type, QString preferred_file_name)
|
||||
-> ModPlatform::IndexedVersion
|
||||
{
|
||||
ModPlatform::IndexedVersion file;
|
||||
|
||||
@ -141,6 +142,12 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject &obj, QString preferred_hash_t
|
||||
auto files = Json::requireArray(obj, "files");
|
||||
int i = 0;
|
||||
|
||||
if (files.empty()) {
|
||||
// This should not happen normally, but check just in case
|
||||
qWarning() << "Modrinth returned an unexpected empty list of files:" << obj;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Find correct file (needed in cases where one version may have multiple files)
|
||||
// Will default to the last one if there's no primary (though I think Modrinth requires that
|
||||
// at least one file is primary, idk)
|
||||
@ -167,7 +174,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject &obj, QString preferred_hash_t
|
||||
file.fileName = Json::requireString(parent, "filename");
|
||||
file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1);
|
||||
auto hash_list = Json::requireObject(parent, "hashes");
|
||||
|
||||
|
||||
if (hash_list.contains(preferred_hash_type)) {
|
||||
file.hash = Json::requireString(hash_list, preferred_hash_type);
|
||||
file.hash_type = preferred_hash_type;
|
||||
|
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -93,4 +93,36 @@
|
||||
inkscape:label="beak" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="m16.746 6.9737 2.8639 4.9609c0.33073 0 2.6991-1.3672 2.8644-1.6536 0.16536-0.28642 0.16536-3.0209 0-3.3073l-2.8644 1.6536z" fill="#dfdfdf" stroke-width=".26458"/>
|
||||
</g>
|
||||
<path d="m3.8299 4.8948c-0.14551 0.25205-0.14553 2.6584 0 2.9104 0.14553 0.25204 2.2292 1.4552 2.5203 1.4552v-2.9104z" fill="#d6d2d2" stroke-width=".26458"/>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:title>Prism Launcher Logo</dc:title>
|
||||
<dc:date>19/10/2022</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:source>https://github.com/PrismLauncher/PrismLauncher</dc:source>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA 4.0</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>Prism Launcher</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -153,7 +153,7 @@ QString Task::describe()
|
||||
auto name = objectName();
|
||||
if(name.isEmpty())
|
||||
{
|
||||
out << QString("0x%1").arg((quintptr)this, 0, 16);
|
||||
out << QString("0x%1").arg(reinterpret_cast<quintptr>(this), 0, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -83,6 +83,12 @@ struct Language
|
||||
else if(key == "es_UY") {
|
||||
result = u8"español de Latinoamérica";
|
||||
}
|
||||
else if(key == "en@pirate") {
|
||||
result = u8"Tongue of the High Seas";
|
||||
}
|
||||
else if(key == "en@uwu") {
|
||||
result = u8"Cute Engwish";
|
||||
}
|
||||
else {
|
||||
result = locale.nativeLanguageName();
|
||||
}
|
||||
|
@ -335,11 +335,10 @@ public:
|
||||
all_actions.append(&actionSettings);
|
||||
|
||||
actionUndoTrashInstance = TranslatedAction(MainWindow);
|
||||
connect(actionUndoTrashInstance, SIGNAL(triggered(bool)), MainWindow, SLOT(undoTrashInstance()));
|
||||
actionUndoTrashInstance->setObjectName(QStringLiteral("actionUndoTrashInstance"));
|
||||
actionUndoTrashInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Undo Last Instance Deletion"));
|
||||
actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
|
||||
actionUndoTrashInstance->setShortcut(QKeySequence("Ctrl+Z"));
|
||||
actionUndoTrashInstance->setShortcut(QKeySequence::Undo);
|
||||
all_actions.append(&actionUndoTrashInstance);
|
||||
|
||||
actionClearMetadata = TranslatedAction(MainWindow);
|
||||
@ -527,7 +526,7 @@ public:
|
||||
|
||||
menuBar->addMenu(foldersMenu);
|
||||
|
||||
profileMenu = menuBar->addMenu(tr("&Profiles"));
|
||||
profileMenu = menuBar->addMenu(tr("&Accounts"));
|
||||
profileMenu->setSeparatorsCollapsible(false);
|
||||
profileMenu->addAction(actionManageAccounts);
|
||||
|
||||
@ -1003,6 +1002,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->actionUndoTrashInstance.operator->(), &QAction::triggered, this, &MainWindow::undoTrashInstance);
|
||||
|
||||
setSelectedInstanceById(APPLICATION->settings()->get("SelectedInstance").toString());
|
||||
|
||||
// removing this looks stupid
|
||||
@ -1032,7 +1033,7 @@ void MainWindow::retranslateUi()
|
||||
accountMenuButton->setText(profileLabel);
|
||||
}
|
||||
else {
|
||||
accountMenuButton->setText(tr("Profiles"));
|
||||
accountMenuButton->setText(tr("Accounts"));
|
||||
}
|
||||
|
||||
if (m_selectedInstance) {
|
||||
@ -1114,11 +1115,6 @@ void MainWindow::showInstanceContextMenu(const QPoint &pos)
|
||||
connect(actionDeleteGroup, SIGNAL(triggered(bool)), SLOT(deleteGroup()));
|
||||
actions.append(actionDeleteGroup);
|
||||
}
|
||||
|
||||
QAction *actionUndoTrashInstance = new QAction("Undo last trash instance", this);
|
||||
connect(actionUndoTrashInstance, SIGNAL(triggered(bool)), SLOT(undoTrashInstance()));
|
||||
actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
|
||||
actions.append(actionUndoTrashInstance);
|
||||
}
|
||||
QMenu myMenu;
|
||||
myMenu.addActions(actions);
|
||||
@ -1375,7 +1371,7 @@ void MainWindow::defaultAccountChanged()
|
||||
|
||||
// Set the icon to the "no account" icon.
|
||||
accountMenuButton->setIcon(APPLICATION->getThemedIcon("noaccount"));
|
||||
accountMenuButton->setText(tr("Profiles"));
|
||||
accountMenuButton->setText(tr("Accounts"));
|
||||
}
|
||||
|
||||
bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
|
||||
@ -1814,6 +1810,7 @@ void MainWindow::deleteGroup()
|
||||
void MainWindow::undoTrashInstance()
|
||||
{
|
||||
APPLICATION->instances()->undoTrashInstance();
|
||||
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewInstanceFolder_triggered()
|
||||
@ -1920,6 +1917,7 @@ void MainWindow::on_actionDeleteInstance_triggered()
|
||||
|
||||
auto id = m_selectedInstance->id();
|
||||
if (APPLICATION->instances()->trashInstance(id)) {
|
||||
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ QString getCreditsHtml()
|
||||
stream << "<br />\n";
|
||||
|
||||
stream << "<h3>" << QObject::tr("With thanks to", "About Credits") << "</h3>\n";
|
||||
stream << QString("<p>Boba %1</p>\n") .arg(getWebsite("https://cmdplusv.neocities.org/"));
|
||||
stream << QString("<p>Boba %1</p>\n") .arg(getWebsite("https://bobaonline.neocities.org/"));
|
||||
stream << QString("<p>Davi Rafael %1</p>\n") .arg(getWebsite("https://auti.one/"));
|
||||
stream << QString("<p>Fulmine %1</p>\n") .arg(getWebsite("https://www.fulmine.xyz/"));
|
||||
stream << QString("<p>ely %1</p>\n") .arg(getGitHub("elyrodso"));
|
||||
|
@ -87,14 +87,11 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QLabel" name="versionLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
@ -167,7 +164,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QLabel" name="platformLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
@ -183,7 +180,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QLabel" name="buildDateLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
@ -199,7 +196,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QLabel" name="commitLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
@ -215,7 +212,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QLabel" name="channelLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
|
@ -366,33 +366,28 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info)
|
||||
auto changelog = new QTreeWidgetItem(changelog_item);
|
||||
auto changelog_area = new QTextBrowser();
|
||||
|
||||
QString text = info.changelog;
|
||||
switch (info.provider) {
|
||||
case ModPlatform::Provider::MODRINTH: {
|
||||
HoeDown h;
|
||||
// HoeDown bug?: \n aren't converted to <br>
|
||||
auto text = h.process(info.changelog.toUtf8());
|
||||
text = h.process(info.changelog.toUtf8());
|
||||
|
||||
// Don't convert if there's an HTML tag right after (Qt rendering weirdness)
|
||||
text.remove(QRegularExpression("(\n+)(?=<)"));
|
||||
text.replace('\n', "<br>");
|
||||
|
||||
changelog_area->setHtml(text);
|
||||
break;
|
||||
}
|
||||
case ModPlatform::Provider::FLAME: {
|
||||
changelog_area->setHtml(info.changelog);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
changelog_area->setHtml(text);
|
||||
changelog_area->setOpenExternalLinks(true);
|
||||
changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap);
|
||||
changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth);
|
||||
changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
||||
|
||||
// HACK: Is there a better way of achieving this?
|
||||
auto font_height = QFontMetrics(changelog_area->font()).height();
|
||||
changelog_area->setMaximumHeight((changelog_area->toPlainText().count(QRegularExpression("\n|<br>")) + 2) * font_height);
|
||||
|
||||
ui->modTreeWidget->setItemWidget(changelog, 0, changelog_area);
|
||||
|
||||
ui->modTreeWidget->addTopLevelItem(item_top);
|
||||
|
@ -136,11 +136,13 @@ void ProgressDialog::onTaskStarted() {}
|
||||
void ProgressDialog::onTaskFailed(QString failure)
|
||||
{
|
||||
reject();
|
||||
hide();
|
||||
}
|
||||
|
||||
void ProgressDialog::onTaskSucceeded()
|
||||
{
|
||||
accept();
|
||||
hide();
|
||||
}
|
||||
|
||||
void ProgressDialog::changeStatus(const QString& status)
|
||||
|
@ -179,7 +179,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Enter a custom client ID for Microsoft Authentication here. </string>
|
||||
<string>Enter a custom client ID for Microsoft Authentication here.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include <FileSystem.h>
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "DesktopServices.h"
|
||||
#include "ui/themes/ITheme.h"
|
||||
|
||||
#include <QApplication>
|
||||
@ -143,11 +144,11 @@ void LauncherPage::on_instDirBrowseBtn_clicked()
|
||||
ui->instDirTextBox->setText(cooked_dir);
|
||||
}
|
||||
}
|
||||
else if(APPLICATION->isFlatpak() && raw_dir.startsWith("/run/user"))
|
||||
else if(DesktopServices::isFlatpak() && raw_dir.startsWith("/run/user"))
|
||||
{
|
||||
QMessageBox warning;
|
||||
warning.setText(tr("You're trying to specify an instance folder "
|
||||
"which was granted temporaily via Flatpak.\n"
|
||||
"which was granted temporarily via Flatpak.\n"
|
||||
"This is known to cause problems. "
|
||||
"After a restart the launcher might break, "
|
||||
"because it will no longer have access to that directory.\n\n"
|
||||
|
@ -267,18 +267,25 @@ void ListModel::searchRequestFailed(QString reason)
|
||||
.arg(m_parent->displayName())
|
||||
.arg(tr("API version too old!\nPlease update %1!").arg(BuildConfig.LAUNCHER_DISPLAYNAME)));
|
||||
}
|
||||
|
||||
jobPtr.reset();
|
||||
searchState = Finished;
|
||||
}
|
||||
|
||||
void ListModel::searchRequestAborted()
|
||||
{
|
||||
if (searchState != ResetRequested)
|
||||
qCritical() << "Search task in ModModel aborted by an unknown reason!";
|
||||
|
||||
// Retry fetching
|
||||
jobPtr.reset();
|
||||
|
||||
if (searchState == ResetRequested) {
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
endResetModel();
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
endResetModel();
|
||||
|
||||
nextSearchOffset = 0;
|
||||
performPaginatedSearch();
|
||||
} else {
|
||||
searchState = Finished;
|
||||
}
|
||||
nextSearchOffset = 0;
|
||||
performPaginatedSearch();
|
||||
}
|
||||
|
||||
void ListModel::infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index)
|
||||
|
@ -51,6 +51,7 @@ class ListModel : public QAbstractListModel {
|
||||
public slots:
|
||||
void searchRequestFinished(QJsonDocument& doc);
|
||||
void searchRequestFailed(QString reason);
|
||||
void searchRequestAborted();
|
||||
|
||||
void infoRequestFinished(QJsonDocument& doc, ModPlatform::IndexedPack& pack, const QModelIndex& index);
|
||||
|
||||
|
@ -331,7 +331,7 @@ AtlOptionalModDialog::AtlOptionalModDialog(QWidget* parent, ATLauncher::PackVers
|
||||
connect(ui->clearAllButton, &QPushButton::clicked,
|
||||
listModel, &AtlOptionalModListModel::clearAll);
|
||||
connect(ui->installButton, &QPushButton::clicked,
|
||||
this, &QDialog::close);
|
||||
this, &QDialog::accept);
|
||||
}
|
||||
|
||||
AtlOptionalModDialog::~AtlOptionalModDialog() {
|
||||
|
@ -43,10 +43,13 @@ AtlUserInteractionSupportImpl::AtlUserInteractionSupportImpl(QWidget *parent) :
|
||||
{
|
||||
}
|
||||
|
||||
QVector<QString> AtlUserInteractionSupportImpl::chooseOptionalMods(ATLauncher::PackVersion version, QVector<ATLauncher::VersionMod> mods)
|
||||
std::optional<QVector<QString>> AtlUserInteractionSupportImpl::chooseOptionalMods(ATLauncher::PackVersion version, QVector<ATLauncher::VersionMod> mods)
|
||||
{
|
||||
AtlOptionalModDialog optionalModDialog(m_parent, version, mods);
|
||||
optionalModDialog.exec();
|
||||
auto result = optionalModDialog.exec();
|
||||
if (result == QDialog::Rejected) {
|
||||
return {};
|
||||
}
|
||||
return optionalModDialog.getResult();
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
|
||||
private:
|
||||
QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) override;
|
||||
QVector<QString> chooseOptionalMods(ATLauncher::PackVersion version, QVector<ATLauncher::VersionMod> mods) override;
|
||||
std::optional<QVector<QString>> chooseOptionalMods(ATLauncher::PackVersion version, QVector<ATLauncher::VersionMod> mods) override;
|
||||
void displayMessage(QString message) override;
|
||||
|
||||
private:
|
||||
|
@ -31,7 +31,7 @@ QPalette DarkTheme::colorScheme()
|
||||
darkPalette.setColor(QPalette::ButtonText, Qt::white);
|
||||
darkPalette.setColor(QPalette::BrightText, Qt::red);
|
||||
darkPalette.setColor(QPalette::Link, QColor(47,163,198));
|
||||
darkPalette.setColor(QPalette::Highlight, QColor(145,205,92));
|
||||
darkPalette.setColor(QPalette::Highlight, QColor(150,219,89));
|
||||
darkPalette.setColor(QPalette::HighlightedText, Qt::black);
|
||||
darkPalette.setColor(QPalette::PlaceholderText, Qt::darkGray);
|
||||
return fadeInactive(darkPalette, fadeAmount(), fadeColor());
|
||||
|
@ -51,6 +51,8 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
|
||||
auto remaining_width = rect.width() - icon_width - 2 * icon_x_margin;
|
||||
rect.setRect(rect.x() + icon_width + 2 * icon_x_margin, rect.y(), remaining_width, rect.height());
|
||||
|
||||
int title_height = 0;
|
||||
|
||||
{ // Title painting
|
||||
auto title = index.data(UserDataTypes::TITLE).toString();
|
||||
|
||||
@ -66,8 +68,10 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
|
||||
font.setPointSize(font.pointSize() + 2);
|
||||
painter->setFont(font);
|
||||
|
||||
title_height = QFontMetrics(font).height();
|
||||
|
||||
// On the top, aligned to the left after the icon
|
||||
painter->drawText(rect.x(), rect.y() + QFontMetrics(font).height(), title);
|
||||
painter->drawText(rect.x(), rect.y() + title_height, title);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
@ -82,17 +86,38 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
|
||||
|
||||
// Get first line unconditionally
|
||||
description = cut_text.first().second;
|
||||
auto num_lines = 1;
|
||||
|
||||
// Get second line, elided if needed
|
||||
if (cut_text.size() > 1) {
|
||||
if (cut_text.size() > 2)
|
||||
description += opt.fontMetrics.elidedText(cut_text.at(1).second, opt.textElideMode, cut_text.at(1).first);
|
||||
else
|
||||
description += cut_text.at(1).second;
|
||||
// 2.5x so because there should be some margin left from the 2x so things don't get too squishy.
|
||||
if (rect.height() - title_height <= 2.5 * opt.fontMetrics.height()) {
|
||||
// If there's not enough space, show only a single line, elided.
|
||||
description = opt.fontMetrics.elidedText(description, opt.textElideMode, cut_text.at(0).first);
|
||||
} else {
|
||||
if (cut_text.size() > 2) {
|
||||
description += opt.fontMetrics.elidedText(cut_text.at(1).second, opt.textElideMode, cut_text.at(1).first);
|
||||
} else {
|
||||
description += cut_text.at(1).second;
|
||||
}
|
||||
num_lines += 1;
|
||||
}
|
||||
}
|
||||
|
||||
int description_x = rect.x();
|
||||
|
||||
|
||||
// Have the y-value be set based on the number of lines in the description, to centralize the
|
||||
// description text with the space between the base and the title.
|
||||
int description_y = rect.y() + title_height + (rect.height() - title_height) / 2;
|
||||
if (num_lines == 1)
|
||||
description_y -= opt.fontMetrics.height() / 2;
|
||||
else
|
||||
description_y -= opt.fontMetrics.height();
|
||||
|
||||
// On the bottom, aligned to the left after the icon, and featuring at most two lines of text (with some margin space to spare)
|
||||
painter->drawText(rect.x(), rect.y() + rect.height() - 2.2 * opt.fontMetrics.height(), remaining_width,
|
||||
2 * opt.fontMetrics.height(), Qt::TextWordWrap, description);
|
||||
painter->drawText(description_x, description_y, remaining_width,
|
||||
cut_text.size() * opt.fontMetrics.height(), Qt::TextWordWrap, description);
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
|
@ -210,7 +210,7 @@ void LocalPeer::receiveConnection()
|
||||
return;
|
||||
}
|
||||
|
||||
while (socket->bytesAvailable() < (int)sizeof(quint32))
|
||||
while (socket->bytesAvailable() < static_cast<int>(sizeof(quint32)))
|
||||
{
|
||||
socket->waitForReadyRead();
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ void DeviceFlow::onRefreshError(QNetworkReply::NetworkError error, QNetworkReply
|
||||
if(refreshReply) {
|
||||
refreshReply->deleteLater();
|
||||
}
|
||||
qDebug() << "DeviceFlow::onRefreshFinished: Error" << (int)error << " - " << errorString;
|
||||
qDebug() << "DeviceFlow::onRefreshFinished: Error" << static_cast<int>(error) << " - " << errorString;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,12 +55,12 @@ uint32_t MurmurHash2(std::ifstream&& file_stream, std::size_t buffer_size, std::
|
||||
|
||||
// Mix 4 bytes at a time into the hash
|
||||
if (index == 0)
|
||||
FourBytes_MurmurHash2((unsigned char*)&data, info);
|
||||
FourBytes_MurmurHash2(reinterpret_cast<unsigned char*>(&data), info);
|
||||
}
|
||||
} while (!file_stream.eof());
|
||||
|
||||
// Do one last bit shuffle in the hash
|
||||
FourBytes_MurmurHash2((unsigned char*)&data, info);
|
||||
FourBytes_MurmurHash2(reinterpret_cast<unsigned char*>(&data), info);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
@ -72,7 +72,7 @@ void FourBytes_MurmurHash2(const unsigned char* data, IncrementalHashInfo& prev)
|
||||
{
|
||||
if (prev.len >= 4) {
|
||||
// Not the final mix
|
||||
uint32_t k = *(uint32_t*)data;
|
||||
uint32_t k = *reinterpret_cast<const uint32_t*>(data);
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
|
93
nix/NIX.md
@ -1,36 +1,83 @@
|
||||
# How to import
|
||||
# Running on Nix
|
||||
|
||||
To import with flakes use
|
||||
## Putting it in your system configuration
|
||||
|
||||
### On flakes-enabled nix
|
||||
|
||||
#### Directly installing
|
||||
|
||||
The `prismlauncher` flake provides a package which you can install along with
|
||||
the rest of your packages
|
||||
|
||||
```nix
|
||||
# In your flake.nix:
|
||||
{
|
||||
inputs = {
|
||||
pollymc.url = "github:fn2006/PollyMC";
|
||||
prismlauncher.url = "github:PrismLauncher/PrismLauncher";
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
nixpkgs.overlays = [ inputs.pollymc.overlay ]; ## Within configuration.nix
|
||||
environment.systemPackages = with pkgs; [ pollymc ]; ##
|
||||
}
|
||||
```
|
||||
|
||||
To import without flakes use channels:
|
||||
|
||||
```sh
|
||||
nix-channel --add https://github.com/fn2006/PollyMC/archive/master.tar.gz pollymc
|
||||
nix-channel --update pollymc
|
||||
nix-env -iA pollymc
|
||||
```
|
||||
|
||||
or alternatively you can use
|
||||
|
||||
```nix
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(import (builtins.fetchTarball "https://github.com/fn2006/PollyMC/archive/develop.tar.gz")).overlay
|
||||
];
|
||||
# And in your system configuration:
|
||||
environment.systemPackages = [ prismlauncher.packages.${pkgs.system}.prismlauncher ];
|
||||
|
||||
environment.systemPackages = with pkgs; [ pollymc ];
|
||||
# Or in your home-manager configuration:
|
||||
home.packages = [ prismlauncher.packages.${pkgs.system}.prismlauncher ];
|
||||
```
|
||||
|
||||
#### Using the overlay
|
||||
|
||||
Alternatively, you can overlay the prismlauncher version in nixpkgs which will
|
||||
allow you to install using `pkgs` as you normally would while also using the
|
||||
latest version
|
||||
|
||||
```nix
|
||||
# In your flake.nix:
|
||||
{
|
||||
inputs = {
|
||||
prismlauncher.url = "github:PrismLauncher/PrismLauncher";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```nix
|
||||
# And in your system configuration:
|
||||
nixpkgs.overlays = [ inputs.prismlauncher.overlay ];
|
||||
environment.systemPackages = [ pkgs.prismlauncher ];
|
||||
|
||||
# Or in your home-manager configuration:
|
||||
config.nixpkgs.overlays = [ inputs.prismlauncher.overlay ];
|
||||
home.packages = [ pkgs.prismlauncher ];
|
||||
```
|
||||
|
||||
### Without flakes-enabled nix
|
||||
|
||||
#### Using channels
|
||||
|
||||
```sh
|
||||
nix-channel --add https://github.com/PrismLauncher/PrismLauncher/archive/master.tar.gz prismlauncher
|
||||
nix-channel --update prismlauncher
|
||||
nix-env -iA prismlauncher
|
||||
```
|
||||
|
||||
#### Using the overlay
|
||||
|
||||
```nix
|
||||
# In your configuration.nix:
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(import (builtins.fetchTarball "https://github.com/PrismLauncher/PrismLauncher/archive/develop.tar.gz")).overlay
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [ prismlauncher ];
|
||||
}
|
||||
```
|
||||
|
||||
## Running ad-hoc
|
||||
|
||||
If you're on a flakes-enabled nix you can run the launcher in one-line
|
||||
|
||||
```sh
|
||||
nix run github:PrismLauncher/PrismLauncher
|
||||
```
|
||||
|
@ -59,20 +59,20 @@ stdenv.mkDerivation rec {
|
||||
# Copy libnbtplusplus
|
||||
rm -rf source/libraries/libnbtplusplus
|
||||
mkdir source/libraries/libnbtplusplus
|
||||
cp -a ${libnbtplusplus}/* source/libraries/libnbtplusplus
|
||||
chmod a+r+w source/libraries/libnbtplusplus/*
|
||||
ln -s ${libnbtplusplus}/* source/libraries/libnbtplusplus
|
||||
chmod -R +r+w source/libraries/libnbtplusplus
|
||||
# Copy tomlplusplus
|
||||
rm -rf source/libraries/tomlplusplus
|
||||
mkdir source/libraries/tomlplusplus
|
||||
cp -a ${tomlplusplus}/* source/libraries/tomlplusplus
|
||||
chmod a+r+w source/libraries/tomlplusplus/*
|
||||
ln -s ${tomlplusplus}/* source/libraries/tomlplusplus
|
||||
chmod -R +r+w source/libraries/tomlplusplus
|
||||
'';
|
||||
|
||||
cmakeFlags = [
|
||||
"-GNinja"
|
||||
"-DLauncher_QT_VERSION_MAJOR=${lib.versions.major qtbase.version}"
|
||||
] ++ lib.optionals enableLTO [ "-DENABLE_LTO=on" ]
|
||||
++ lib.optionals (msaClientID != "") [ "-DLauncher_MSA_CLIENT_ID=${msaClientID}" ];
|
||||
++ lib.optionals (msaClientID != "") [ "-DLauncher_MSA_CLIENT_ID=${msaClientID}" ];
|
||||
|
||||
# we have to check if the system is NixOS before adding stdenv.cc.cc.lib (#923)
|
||||
postInstall = ''
|
||||
@ -96,6 +96,6 @@ stdenv.mkDerivation rec {
|
||||
'';
|
||||
platforms = platforms.unix;
|
||||
license = licenses.gpl3Only;
|
||||
maintainers = with maintainers; [ starcraft66 kloenk ];
|
||||
maintainers = with maintainers; [ minion3665 Scrumplex ];
|
||||
};
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ set(Launcher_Git "https://github.com/fn2006/PollyMC" PARENT_SCOPE)
|
||||
set(Launcher_DesktopFileName "org.fn2006.PollyMC.desktop" PARENT_SCOPE)
|
||||
set(Launcher_SVGFileName "org.fn2006.PollyMC.svg" PARENT_SCOPE)
|
||||
|
||||
set(Launcher_Desktop "program_info/org.fn2006.PollyMC.desktop" PARENT_SCOPE)
|
||||
set(Launcher_Desktop "program_info/${Launcher_DesktopFileName}" PARENT_SCOPE)
|
||||
set(Launcher_MetaInfo "program_info/org.fn2006.PollyMC.metainfo.xml" PARENT_SCOPE)
|
||||
set(Launcher_SVG "program_info/org.fn2006.PollyMC.svg" PARENT_SCOPE)
|
||||
set(Launcher_SVG "program_info/${Launcher_SVGFileName}" PARENT_SCOPE)
|
||||
set(Launcher_Branding_ICNS "program_info/prismlauncher.icns" PARENT_SCOPE)
|
||||
set(Launcher_Branding_ICO "program_info/pollymc.ico")
|
||||
set(Launcher_Branding_ICO "${Launcher_Branding_ICO}" PARENT_SCOPE)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# PrismLauncher Program Info
|
||||
# Prism Launcher Program Info
|
||||
|
||||
This is PrismLauncher's program info which contains information about:
|
||||
This is Prism Launcher's program info which contains information about:
|
||||
|
||||
- Application name and logo (and branding in general)
|
||||
- Various URLs and API endpoints
|
||||
|
@ -1,39 +1,73 @@
|
||||
#/bin/bash
|
||||
#!/bin/bash
|
||||
|
||||
# ICO
|
||||
svg2png() {
|
||||
input_file="$1"
|
||||
output_file="$2"
|
||||
width="$3"
|
||||
height="$4"
|
||||
|
||||
inkscape -w 16 -h 16 -o pollymc_16.png org.fn2006.PollyMC.svg
|
||||
inkscape -w 24 -h 24 -o pollymc_24.png org.fn2006.PollyMC.svg
|
||||
inkscape -w 32 -h 32 -o pollymc_32.png org.fn2006.PollyMC.svg
|
||||
inkscape -w 48 -h 48 -o pollymc_48.png org.fn2006.PollyMC.svg
|
||||
inkscape -w 64 -h 64 -o pollymc_64.png org.fn2006.PollyMC.svg
|
||||
inkscape -w 128 -h 128 -o pollymc_128.png org.fn2006.PollyMC.svg
|
||||
inkscape -w "$width" -h "$height" -o "$output_file" "$input_file"
|
||||
}
|
||||
|
||||
convert pollymc_128.png pollymc_64.png pollymc_48.png pollymc_32.png pollymc_24.png pollymc_16.png pollymc.ico
|
||||
sipsresize() {
|
||||
input_file="$1"
|
||||
output_file="$2"
|
||||
width="$3"
|
||||
height="$4"
|
||||
|
||||
rm -f pollymc_*.png
|
||||
sips -z "$width" "$height" "$input_file" --out "$output_file"
|
||||
}
|
||||
|
||||
inkscape -w 1024 -h 1024 -o pollymc_1024.png org.fn2006.PollyMC.bigsur.svg
|
||||
if command -v "inkscape" && command -v "icotool"; then
|
||||
# Windows ICO
|
||||
d=$(mktemp -d)
|
||||
|
||||
mkdir pollymc.iconset
|
||||
svg2png org.prismlauncher.PrismLauncher.svg "$d/prismlauncher_16.png" 16 16
|
||||
svg2png org.prismlauncher.PrismLauncher.svg "$d/prismlauncher_24.png" 24 24
|
||||
svg2png org.prismlauncher.PrismLauncher.svg "$d/prismlauncher_32.png" 32 32
|
||||
svg2png org.prismlauncher.PrismLauncher.svg "$d/prismlauncher_48.png" 48 48
|
||||
svg2png org.prismlauncher.PrismLauncher.svg "$d/prismlauncher_64.png" 64 64
|
||||
svg2png org.prismlauncher.PrismLauncher.svg "$d/prismlauncher_128.png" 128 128
|
||||
svg2png org.prismlauncher.PrismLauncher.svg "$d/prismlauncher_256.png" 256 256
|
||||
|
||||
magick pollymc_1024.png -resize 16x16 pollymc.iconset/icon_16x16.png
|
||||
magick pollymc_1024.png -resize 32x32 pollymc.iconset/icon_16x16@2x.png
|
||||
magick pollymc_1024.png -resize 32x32 pollymc.iconset/icon_32x32.png
|
||||
magick pollymc_1024.png -resize 64x64 pollymc.iconset/icon_32x32@2x.png
|
||||
magick pollymc_1024.png -resize 128x128 pollymc.iconset/icon_128x128.png
|
||||
magick pollymc_1024.png -resize 256x256 pollymc.iconset/icon_128x128@2x.png
|
||||
magick pollymc_1024.png -resize 256x256 pollymc.iconset/icon_256x256.png
|
||||
magick pollymc_1024.png -resize 512x512 pollymc.iconset/icon_256x256@2x.png
|
||||
magick pollymc_1024.png -resize 512x512 pollymc.iconset/icon_512x512.png
|
||||
cp pollymc_1024.png pollymc.iconset/icon_512x512@2x.png
|
||||
rm prismlauncher.ico && icotool -o prismlauncher.ico -c \
|
||||
"$d/prismlauncher_256.png" \
|
||||
"$d/prismlauncher_128.png" \
|
||||
"$d/prismlauncher_64.png" \
|
||||
"$d/prismlauncher_48.png" \
|
||||
"$d/prismlauncher_32.png" \
|
||||
"$d/prismlauncher_24.png" \
|
||||
"$d/prismlauncher_16.png"
|
||||
else
|
||||
echo "ERROR: Windows icons were NOT generated!" >&2
|
||||
echo "ERROR: requires inkscape and icotool in PATH"
|
||||
fi
|
||||
|
||||
icnsify -i pollymc_1024.png -o pollymc.icns
|
||||
if command -v "inkscape" && command -v "sips" && command -v "iconutil"; then
|
||||
# macOS ICNS
|
||||
d=$(mktemp -d)
|
||||
|
||||
rm -f pollymc_*.png
|
||||
rm -rf pollymc.iconset
|
||||
d="$d/prismlauncher.iconset"
|
||||
|
||||
mkdir -p "$d"
|
||||
|
||||
svg2png org.prismlauncher.PrismLauncher.bigsur.svg "$d/icon_512x512@2x.png" 1024 1024
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_16x16.png" 16 16
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_16x16@2.png" 32 32
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_32x32.png" 32 32
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_32x32@2.png" 64 64
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_128x128.png" 128 128
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_128x128@2.png" 256 256
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_256x256.png" 256 256
|
||||
sipsresize "$d/icon_512x512@2.png" "$d/icon_256x256@2.png" 512 512
|
||||
iconutil -c icns "$d"
|
||||
else
|
||||
echo "ERROR: macOS icons were NOT generated!" >&2
|
||||
echo "ERROR: requires inkscape, sips and iconutil in PATH"
|
||||
fi
|
||||
|
||||
# replace icon in themes
|
||||
for dir in ../launcher/resources/*/scalable
|
||||
do
|
||||
cp -v org.fn2006.PollyMC.svg $dir/launcher.svg
|
||||
cp -v org.prismlauncher.PrismLauncher.svg "$dir/launcher.svg"
|
||||
done
|
||||
|
@ -7,6 +7,6 @@ Terminal=false
|
||||
Exec=@Launcher_APP_BINARY_NAME@
|
||||
StartupNotify=true
|
||||
Icon=org.prismlauncher.PrismLauncher
|
||||
Categories=Game;
|
||||
Keywords=game;minecraft;launcher;mc;
|
||||
Categories=Game;ActionGame;AdventureGame;Simulation;
|
||||
Keywords=game;minecraft;launcher;mc;multimc;polymc;
|
||||
StartupWMClass=PrismLauncher
|
||||
|
@ -1,33 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>org.prismlauncher.PrismLauncher</id>
|
||||
<provides>
|
||||
<id>org.prismlauncher.PrismLauncher</id>
|
||||
</provides>
|
||||
<launchable type="desktop-id">org.prismlauncher.PrismLauncher.desktop</launchable>
|
||||
<name>PrismLauncher</name>
|
||||
<developer_name>PrismLauncher</developer_name>
|
||||
<name>Prism Launcher</name>
|
||||
<developer_name>Prism Launcher Contributors</developer_name>
|
||||
<summary>A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0-only</project_license>
|
||||
<url type="homepage">https://prismlauncher.org/</url>
|
||||
<url type="help">https://prismlauncher.org/wiki/</url>
|
||||
<url type="bugtracker">https://github.com/PrismLauncher/PrismLauncher/issues</url>
|
||||
<url type="contact">https://discord.gg/prismlauncher</url>
|
||||
<url type="vcs-browser">https://github.com/PrismLauncher/PrismLauncher</url>
|
||||
<url type="contribute">https://github.com/PrismLauncher/PrismLauncher/blob/develop/CONTRIBUTING.md</url>
|
||||
<url type="translate">https://hosted.weblate.org/projects/prismlauncher/launcher</url>
|
||||
<description>
|
||||
<p>PrismLauncher is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.</p>
|
||||
<p>Prism Launcher is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.</p>
|
||||
<p>Features:</p>
|
||||
<ul>
|
||||
<li>Easily install game modifications, such as Fabric, Forge and Quilt</li>
|
||||
<li>Control your java settings</li>
|
||||
<li>Control your Java settings</li>
|
||||
<li>Manage worlds and resource packs from the launcher</li>
|
||||
<li>See logs and other details easily</li>
|
||||
<li>Kill Minecraft in case of a crash/freeze</li>
|
||||
<li>Isolate minecraft instances to keep everything clean</li>
|
||||
<li>Isolate Minecraft instances to keep everything clean</li>
|
||||
<li>Install and update mods directly from the launcher</li>
|
||||
</ul>
|
||||
</description>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>The main PrismLauncher window</caption>
|
||||
<caption>The main Prism Launcher window</caption>
|
||||
<image type="source" width="976" height="764">https://prismlauncher.org/img/screenshots/LauncherDark.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 364 KiB |
@ -10,7 +10,7 @@
|
||||
</trustInfo>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<description>Custom Minecraft launcher for managing multiple installs.</description>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Unicode true
|
||||
|
||||
Name "@Launcher_CommonName@"
|
||||
Name "@Launcher_DisplayName@"
|
||||
InstallDir "$LOCALAPPDATA\Programs\@Launcher_CommonName@"
|
||||
InstallDirRegKey HKCU "Software\@Launcher_CommonName@" "InstallDir"
|
||||
RequestExecutionLevel user
|
||||
@ -113,7 +113,7 @@ VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "@Launcher_VERSION_NAME4@
|
||||
;--------------------------------
|
||||
|
||||
; The stuff to install
|
||||
Section "@Launcher_CommonName@"
|
||||
Section "@Launcher_DisplayName@"
|
||||
|
||||
SectionIn RO
|
||||
|
||||
|
@ -183,6 +183,32 @@ slots:
|
||||
f();
|
||||
}
|
||||
|
||||
void test_copy_single_file()
|
||||
{
|
||||
QTemporaryDir tempDir;
|
||||
tempDir.setAutoRemove(true);
|
||||
|
||||
{
|
||||
QString file = QFINDTESTDATA("testdata/FileSystem/test_folder/pack.mcmeta");
|
||||
|
||||
qDebug() << "From:" << file << "To:" << tempDir.path();
|
||||
|
||||
QDir target_dir(FS::PathCombine(tempDir.path(), "pack.mcmeta"));
|
||||
qDebug() << tempDir.path();
|
||||
qDebug() << target_dir.path();
|
||||
FS::copy c(file, target_dir.filePath("pack.mcmeta"));
|
||||
c();
|
||||
|
||||
auto filter = QDir::Filter::Files;
|
||||
|
||||
for (auto entry: target_dir.entryList(filter)) {
|
||||
qDebug() << entry;
|
||||
}
|
||||
|
||||
QVERIFY(target_dir.entryList(filter).contains("pack.mcmeta"));
|
||||
}
|
||||
}
|
||||
|
||||
void test_getDesktop()
|
||||
{
|
||||
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
||||
|