From 7b1e68bfa8cf9d46e0c810cfa0f077427cfc707a Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Wed, 19 Oct 2022 21:53:57 +0800 Subject: [PATCH 001/277] initial support for add to PATH action Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/FileSystem.cpp | 13 +++++++++++++ launcher/FileSystem.h | 5 +++++ launcher/ui/MainWindow.cpp | 29 +++++++++++++++++++++++++++++ launcher/ui/MainWindow.h | 2 ++ 4 files changed, 49 insertions(+) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 4026d6c1..13bb95d9 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -163,6 +163,19 @@ bool ensureFolderPathExists(QString foldernamepath) return success; } +bool symlink(const QString& source, const QString& target) +{ + std::error_code err; + + fs::create_symlink(toStdString(source), toStdString(target)); + + if (err) { + qWarning() << "Failed to symlink files:" << QString::fromStdString(err.message()); + } + + return err.value() == 0; +} + bool copy::operator()(const QString& offset) { using copy_opts = fs::copy_options; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index b46f3281..2d4dfb56 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -53,6 +53,11 @@ class FileSystemException : public ::Exception { */ void write(const QString& filename, const QByteArray& data); +/** + * create a symlink + */ +bool symlink(const QString& target, const QString& link); + /** * read data from a file safely\ */ diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index afbc505e..80d212e9 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -253,6 +254,7 @@ public: QMenu * helpMenu = nullptr; TranslatedToolButton helpMenuButton; TranslatedAction actionClearMetadata; + TranslatedAction actionAddToPATH; TranslatedAction actionReportBug; TranslatedAction actionDISCORD; TranslatedAction actionMATRIX; @@ -348,6 +350,14 @@ public: actionClearMetadata.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Clear cached metadata")); all_actions.append(&actionClearMetadata); + #ifdef Q_OS_MAC + actionAddToPATH = TranslatedAction(MainWindow); + actionAddToPATH->setObjectName(QStringLiteral("actionAddToPATH")); + actionAddToPATH.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Add to &PATH")); + actionAddToPATH.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Add the prism binary to PATH.")); + all_actions.append(&actionAddToPATH); + #endif + if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) { actionReportBug = TranslatedAction(MainWindow); actionReportBug->setObjectName(QStringLiteral("actionReportBug")); @@ -448,6 +458,10 @@ public: helpMenu->addAction(actionClearMetadata); + #ifdef Q_OS_MAC + helpMenu->addAction(actionAddToPATH); + #endif + if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) { helpMenu->addAction(actionReportBug); } @@ -533,6 +547,9 @@ public: helpMenu = menuBar->addMenu(tr("&Help")); helpMenu->setSeparatorsCollapsible(false); helpMenu->addAction(actionClearMetadata); + #ifdef Q_OS_MAC + helpMenu->addAction(actionAddToPATH); + #endif helpMenu->addSeparator(); helpMenu->addAction(actionAbout); helpMenu->addAction(actionOpenWiki); @@ -1902,6 +1919,18 @@ void MainWindow::on_actionClearMetadata_triggered() APPLICATION->metacache()->evictAll(); } +void MainWindow::on_actionAddToPATH_triggered() { + auto binaryPath = APPLICATION->arguments().first(); + + auto outcome = FS::symlink(binaryPath, "/usr/local/bin/prism"); + + if (!outcome) { + QMessageBox::critical(this, tr("Failed to add Prism to PATH"), tr("")); + } else { + QMessageBox::information(this, tr("Added Prism to PATH"), tr("Prism was successfully added to your PATH.")); + } +} + void MainWindow::on_actionOpenWiki_triggered() { DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg(""))); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index cb8cb4aa..097b0983 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -128,6 +128,8 @@ private slots: void on_actionClearMetadata_triggered(); + void on_actionAddToPATH_triggered(); + void on_actionOpenWiki_triggered(); void on_actionMoreNews_triggered(); From cf3aad9c414782996b11cb0779a9c3d7252b5420 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 5 Nov 2022 22:15:31 +0800 Subject: [PATCH 002/277] fix: use `osascript` to get admin privileges inspired from VSCode's approach Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 80d212e9..beb73c81 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1920,14 +1920,15 @@ void MainWindow::on_actionClearMetadata_triggered() } void MainWindow::on_actionAddToPATH_triggered() { - auto binaryPath = APPLICATION->arguments().first(); + auto binaryPath = APPLICATION->applicationFilePath(); - auto outcome = FS::symlink(binaryPath, "/usr/local/bin/prism"); + qDebug() << "Symlinking" << binaryPath << "to /usr/local/bin/prism"; + auto outcome = QProcess::execute("/usr/bin/osascript", QStringList()<< "-e" << tr("do shell script \"mkdir -p /usr/local/bin && ln -sf '%1' '/usr/local/bin/prismlauncher'\" with administrator privileges").arg(binaryPath)); if (!outcome) { - QMessageBox::critical(this, tr("Failed to add Prism to PATH"), tr("")); + QMessageBox::information(this, tr("Added Prism to PATH"), tr("Prism was successfully added to your PATH. You can now run it with `prismlauncher` in your Terminal. Enjoy!")); } else { - QMessageBox::information(this, tr("Added Prism to PATH"), tr("Prism was successfully added to your PATH.")); + QMessageBox::critical(this, tr("Failed to add Prism to PATH"), tr("Failed to add Prism to PATH :(")); } } From f1d3481a2907cbc72d99321f645c4a827ed436d2 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 5 Nov 2022 22:25:22 +0800 Subject: [PATCH 003/277] fix: remove dumb `FS::symlink` function Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/FileSystem.cpp | 13 ------------- launcher/FileSystem.h | 5 ----- 2 files changed, 18 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 13bb95d9..4026d6c1 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -163,19 +163,6 @@ bool ensureFolderPathExists(QString foldernamepath) return success; } -bool symlink(const QString& source, const QString& target) -{ - std::error_code err; - - fs::create_symlink(toStdString(source), toStdString(target)); - - if (err) { - qWarning() << "Failed to symlink files:" << QString::fromStdString(err.message()); - } - - return err.value() == 0; -} - bool copy::operator()(const QString& offset) { using copy_opts = fs::copy_options; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index 2d4dfb56..b46f3281 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -53,11 +53,6 @@ class FileSystemException : public ::Exception { */ void write(const QString& filename, const QByteArray& data); -/** - * create a symlink - */ -bool symlink(const QString& target, const QString& link); - /** * read data from a file safely\ */ From 3df31579a1b305c9e976f8a21e156a0dd349f40d Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 5 Nov 2022 22:31:07 +0800 Subject: [PATCH 004/277] fix: @timoreo22 Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 4 ++++ launcher/ui/MainWindow.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index beb73c81..c376f4ec 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -254,7 +254,9 @@ public: QMenu * helpMenu = nullptr; TranslatedToolButton helpMenuButton; TranslatedAction actionClearMetadata; + #ifdef Q_OS_MAC TranslatedAction actionAddToPATH; + #endif TranslatedAction actionReportBug; TranslatedAction actionDISCORD; TranslatedAction actionMATRIX; @@ -1919,6 +1921,7 @@ void MainWindow::on_actionClearMetadata_triggered() APPLICATION->metacache()->evictAll(); } +#ifdef Q_OS_MAC void MainWindow::on_actionAddToPATH_triggered() { auto binaryPath = APPLICATION->applicationFilePath(); @@ -1931,6 +1934,7 @@ void MainWindow::on_actionAddToPATH_triggered() { QMessageBox::critical(this, tr("Failed to add Prism to PATH"), tr("Failed to add Prism to PATH :(")); } } +#endif void MainWindow::on_actionOpenWiki_triggered() { diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 097b0983..c9ab8bfb 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -128,7 +128,9 @@ private slots: void on_actionClearMetadata_triggered(); + #ifdef Q_OS_MAC void on_actionAddToPATH_triggered(); + #endif void on_actionOpenWiki_triggered(); From e68dcea6bcb5830659d17db40fc9a83a4eca9cc0 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 24 Oct 2022 18:21:26 +0100 Subject: [PATCH 005/277] Various tweaks to the Java component of the launcher Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 13 +- libraries/README.md | 6 +- libraries/launcher/CMakeLists.txt | 4 +- .../org/prismlauncher/EntryPoint.java | 52 ++--- .../launcher/org/prismlauncher/Launcher.java | 2 +- .../org/prismlauncher/LauncherFactory.java | 19 +- .../org/prismlauncher/applet/LegacyFrame.java | 3 +- .../prismlauncher/impl/AbstractLauncher.java | 95 +++++++++ .../prismlauncher/impl/LegacyLauncher.java | 104 ++++++++++ .../prismlauncher/impl/OneSixLauncher.java | 190 ------------------ .../prismlauncher/impl/StandardLauncher.java | 51 +++++ .../org/prismlauncher/utils/Parameters.java | 12 +- .../org/prismlauncher/utils/Utils.java | 2 +- 13 files changed, 316 insertions(+), 237 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/impl/AbstractLauncher.java create mode 100644 libraries/launcher/org/prismlauncher/impl/LegacyLauncher.java delete mode 100644 libraries/launcher/org/prismlauncher/impl/OneSixLauncher.java create mode 100644 libraries/launcher/org/prismlauncher/impl/StandardLauncher.java diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 3a820951..5a5245ed 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -3,6 +3,7 @@ * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield + * Copyright (C) 2022 TheKodeToad * * 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 @@ -647,7 +648,17 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS { launchScript += "traits " + trait + "\n"; } - launchScript += "launcher onesix\n"; + + launchScript += "launcher "; + + // use legacy launcher if the traits are set + if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch")) + launchScript += "legacy"; + else + launchScript += "standard"; + + launchScript += "\n"; + // qDebug() << "Generated launch script:" << launchScript; return launchScript; } diff --git a/libraries/README.md b/libraries/README.md index dc38477b..2971e32b 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -51,10 +51,10 @@ It: This means the process is essentially idle until the final command is sent. You can, for example, attach a profiler before you send it. -A `legacy` and `onesix` launchers are available. +A `legacy` and `standard` launchers are available. - `legacy` is intended for use with Minecraft versions < 1.6 and is deprecated. -- `onesix` can handle launching any Minecraft version, at the cost of some extra features `legacy` enables (custom window icon and title). +- `standard` can handle launching any Minecraft version, at the cost of some extra features `legacy` enables (custom window icon and title). Example (some parts have been censored): @@ -132,7 +132,7 @@ ext /home/peterix/minecraft/FTB/libraries/org/lwjgl/lwjgl/lwjgl-platform/2.9.1/l ext /home/peterix/minecraft/FTB/libraries/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar natives /home/peterix/minecraft/FTB/17ForgeTest/natives cp /home/peterix/minecraft/FTB/versions/1.7.10/1.7.10.jar -launcher onesix +launcher standard ``` Available under `GPL-3.0-only` (with classpath exception), sublicensed from its original `Apache-2.0` codebase diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index df25414f..ee88d0f9 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -10,7 +10,9 @@ set(SRC org/prismlauncher/EntryPoint.java org/prismlauncher/Launcher.java org/prismlauncher/LauncherFactory.java - org/prismlauncher/impl/OneSixLauncher.java + org/prismlauncher/impl/AbstractLauncher.java + org/prismlauncher/impl/LegacyLauncher.java + org/prismlauncher/impl/StandardLauncher.java org/prismlauncher/applet/LegacyFrame.java org/prismlauncher/exception/ParameterNotFoundException.java org/prismlauncher/exception/ParseException.java diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 9144e1f1..73ff9753 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -81,33 +81,35 @@ public final class EntryPoint { } private Action parseLine(String inData) throws ParseException { - String[] tokens = inData.split("\\s+", 2); - - if (tokens.length == 0) + if (inData.length() == 0) throw new ParseException("Unexpected empty string!"); - switch (tokens[0]) { - case "launch": { - return Action.Launch; - } + String first = inData; + String second = null; + int splitPoint = inData.indexOf(' '); - case "abort": { - return Action.Abort; - } + if (splitPoint != -1) { + first = first.substring(0, splitPoint); + second = inData.substring(splitPoint + 1); + } - default: { - if (tokens.length != 2) + switch (first) { + case "launch": + return Action.LAUNCH; + case "abort": + return Action.ABORT; + default: + if (second == null || second.isEmpty()) throw new ParseException("Error while parsing:" + inData); - params.add(tokens[0], tokens[1]); + params.add(first, second); - return Action.Proceed; - } + return Action.PROCEED; } } public int listen() { - Action action = Action.Proceed; + Action action = Action.PROCEED; try (BufferedReader reader = new BufferedReader(new InputStreamReader( System.in, @@ -115,21 +117,21 @@ public final class EntryPoint { ))) { String line; - while (action == Action.Proceed) { + while (action == Action.PROCEED) { if ((line = reader.readLine()) != null) { action = parseLine(line); } else { - action = Action.Abort; + action = Action.ABORT; } } } catch (IOException | ParseException e) { - LOGGER.log(Level.SEVERE, "Launcher ABORT due to exception:", e); + LOGGER.log(Level.SEVERE, "Launcher abort due to exception:", e); return 1; } // Main loop - if (action == Action.Abort) { + if (action == Action.ABORT) { LOGGER.info("Launch aborted by the launcher."); return 1; @@ -138,7 +140,7 @@ public final class EntryPoint { try { Launcher launcher = LauncherFactory - .getInstance() + .INSTANCE .createLauncher(params); launcher.launch(); @@ -148,7 +150,7 @@ public final class EntryPoint { LOGGER.log(Level.SEVERE, "Wrong argument.", e); return 1; - } catch (Exception e) { + } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); return 1; @@ -156,9 +158,9 @@ public final class EntryPoint { } private enum Action { - Proceed, - Launch, - Abort + PROCEED, + LAUNCH, + ABORT } } diff --git a/libraries/launcher/org/prismlauncher/Launcher.java b/libraries/launcher/org/prismlauncher/Launcher.java index 7f25717b..50c2c9c8 100644 --- a/libraries/launcher/org/prismlauncher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/Launcher.java @@ -18,6 +18,6 @@ package org.prismlauncher; public interface Launcher { - void launch() throws Exception; + void launch() throws Throwable; } diff --git a/libraries/launcher/org/prismlauncher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/LauncherFactory.java index 98f2bbba..354ad1f0 100644 --- a/libraries/launcher/org/prismlauncher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/LauncherFactory.java @@ -35,7 +35,8 @@ package org.prismlauncher; -import org.prismlauncher.impl.OneSixLauncher; +import org.prismlauncher.impl.LegacyLauncher; +import org.prismlauncher.impl.StandardLauncher; import org.prismlauncher.utils.Parameters; import java.util.HashMap; @@ -43,15 +44,21 @@ import java.util.Map; public final class LauncherFactory { - private static final LauncherFactory INSTANCE = new LauncherFactory(); + public static final LauncherFactory INSTANCE = new LauncherFactory(); private final Map launcherRegistry = new HashMap<>(); private LauncherFactory() { - launcherRegistry.put("onesix", new LauncherProvider() { + launcherRegistry.put("standard", new LauncherProvider() { @Override public Launcher provide(Parameters parameters) { - return new OneSixLauncher(parameters); + return new StandardLauncher(parameters); + } + }); + launcherRegistry.put("legacy", new LauncherProvider() { + @Override + public Launcher provide(Parameters parameters) { + return new LegacyLauncher(parameters); } }); } @@ -67,10 +74,6 @@ public final class LauncherFactory { return launcherProvider.provide(parameters); } - public static LauncherFactory getInstance() { - return INSTANCE; - } - public interface LauncherProvider { Launcher provide(Parameters parameters); diff --git a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java b/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java index 4413efa8..f3359fca 100644 --- a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +@SuppressWarnings("removal") public final class LegacyFrame extends Frame { private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); @@ -105,7 +106,7 @@ public final class LegacyFrame extends Frame { appletWrap.setParameter("username", user); appletWrap.setParameter("sessionid", session); - appletWrap.setParameter("stand-alone", "true"); // Show the quit button. + appletWrap.setParameter("stand-alone", "true"); // Show the quit button. TODO: why won't this work? appletWrap.setParameter("haspaid", "true"); // Some old versions need this for world saves to work. appletWrap.setParameter("demo", isDemo ? "true" : "false"); appletWrap.setParameter("fullscreen", "false"); diff --git a/libraries/launcher/org/prismlauncher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/impl/AbstractLauncher.java new file mode 100644 index 00000000..49a984f5 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/impl/AbstractLauncher.java @@ -0,0 +1,95 @@ +/* Copyright 2012-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.prismlauncher.impl; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.prismlauncher.Launcher; +import org.prismlauncher.exception.ParseException; +import org.prismlauncher.utils.Parameters; + +public abstract class AbstractLauncher implements Launcher { + + private static final int DEFAULT_WINDOW_WIDTH = 854; + private static final int DEFAULT_WINDOW_HEIGHT = 480; + + // parameters, separated from ParamBucket + protected final List mcParams; + private final String mainClass; + + // secondary parameters + protected final int width; + protected final int height; + protected final boolean maximize; + + protected final String serverAddress, serverPort; + + protected final ClassLoader classLoader; + + public AbstractLauncher(Parameters params) { + classLoader = ClassLoader.getSystemClassLoader(); + + mcParams = params.allSafe("param", new ArrayList()); + mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); + + serverAddress = params.firstSafe("serverAddress", null); + serverPort = params.firstSafe("serverPort", null); + + String windowParams = params.firstSafe("windowParams", null); + + if ("max".equals(windowParams) || windowParams == null) { + maximize = windowParams != null; + + width = DEFAULT_WINDOW_WIDTH; + height = DEFAULT_WINDOW_HEIGHT; + } else { + maximize = false; + + int byIndex = windowParams.indexOf('x'); + + if (byIndex != -1) { + try { + width = Integer.parseInt(windowParams.substring(0, byIndex)); + height = Integer.parseInt(windowParams.substring(byIndex + 1)); + return; + } catch(NumberFormatException pass) { + } + } + + throw new ParseException("Invalid window size parameter value: " + windowParams); + } + } + + protected Class loadMain() throws ClassNotFoundException { + return classLoader.loadClass(mainClass); + } + + protected void loadAndInvokeMain() throws Throwable, ClassNotFoundException { + invokeMain(loadMain()); + } + + protected void invokeMain(Class mainClass) throws Throwable { + MethodHandle method = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); + + method.invokeExact(mcParams.toArray(new String[0])); + } + +} diff --git a/libraries/launcher/org/prismlauncher/impl/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/impl/LegacyLauncher.java new file mode 100644 index 00000000..30a4dba7 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/impl/LegacyLauncher.java @@ -0,0 +1,104 @@ +/* Copyright 2012-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.prismlauncher.impl; + +import java.applet.Applet; +import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.prismlauncher.applet.LegacyFrame; +import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.Utils; + +@SuppressWarnings("removal") +public final class LegacyLauncher extends AbstractLauncher { + + private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); + + private final String user, session; + private final String title; + private final String appletClass; + + private final boolean noApplet; + private final String cwd; + + public LegacyLauncher(Parameters params) { + super(params); + + user = params.first("userName"); + session = params.first("sessionId"); + title = params.firstSafe("windowTitle", "Minecraft"); + appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); + + List traits = params.allSafe("traits", Collections.emptyList()); + noApplet = traits.contains("noapplet"); + + cwd = System.getProperty("user.dir"); + } + + @Override + public void launch() throws Throwable { + Class main = loadMain(); + Field gameDirField = Utils.getMinecraftGameDirField(main); + + if (gameDirField == null) { + LOGGER.warning("Could not find Mineraft path field."); + } else { + gameDirField.setAccessible(true); + gameDirField.set(null, new File(cwd)); + } + + if (!noApplet) { + LOGGER.info("Launching with applet wrapper..."); + + try { + Class appletClass = classLoader.loadClass(this.appletClass); + + MethodHandle constructor = MethodHandles.lookup().findConstructor(appletClass, MethodType.methodType(void.class)); + Applet applet = (Applet) constructor.invoke(); + + LegacyFrame window = new LegacyFrame(title, applet); + + window.start( + user, + session, + width, + height, + maximize, + serverAddress, + serverPort, + mcParams.contains("--demo") + ); + + return; + } catch (Throwable e) { + LOGGER.log(Level.SEVERE, "Applet wrapper failed:", e); + + LOGGER.warning("Falling back to using main class."); + } + } + + invokeMain(main); + } + +} diff --git a/libraries/launcher/org/prismlauncher/impl/OneSixLauncher.java b/libraries/launcher/org/prismlauncher/impl/OneSixLauncher.java deleted file mode 100644 index d6443826..00000000 --- a/libraries/launcher/org/prismlauncher/impl/OneSixLauncher.java +++ /dev/null @@ -1,190 +0,0 @@ -/* Copyright 2012-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.prismlauncher.impl; - -import org.prismlauncher.Launcher; -import org.prismlauncher.applet.LegacyFrame; -import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.Utils; - -import java.applet.Applet; -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.List; -import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -public final class OneSixLauncher implements Launcher { - - private static final int DEFAULT_WINDOW_WIDTH = 854; - private static final int DEFAULT_WINDOW_HEIGHT = 480; - - private static final Logger LOGGER = Logger.getLogger("OneSixLauncher"); - - // parameters, separated from ParamBucket - private final List mcParams; - private final List traits; - private final String appletClass; - private final String mainClass; - private final String userName, sessionId; - private final String windowTitle; - - // secondary parameters - private final int winSizeW; - private final int winSizeH; - private final boolean maximize; - private final String cwd; - - private final String serverAddress; - private final String serverPort; - - private final ClassLoader classLoader; - - public OneSixLauncher(Parameters params) { - classLoader = ClassLoader.getSystemClassLoader(); - - mcParams = params.allSafe("param", new ArrayList()); - mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); - appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); - traits = params.allSafe("traits", new ArrayList()); - - userName = params.first("userName"); - sessionId = params.first("sessionId"); - windowTitle = params.firstSafe("windowTitle", "Minecraft"); - - serverAddress = params.firstSafe("serverAddress", null); - serverPort = params.firstSafe("serverPort", null); - - cwd = System.getProperty("user.dir"); - - String windowParams = params.firstSafe("windowParams", null); - - if (windowParams != null) { - String[] dimStrings = windowParams.split("x"); - - if (windowParams.equalsIgnoreCase("max")) { - maximize = true; - - winSizeW = DEFAULT_WINDOW_WIDTH; - winSizeH = DEFAULT_WINDOW_HEIGHT; - } else if (dimStrings.length == 2) { - maximize = false; - - winSizeW = Integer.parseInt(dimStrings[0]); - winSizeH = Integer.parseInt(dimStrings[1]); - } else { - throw new IllegalArgumentException("Unexpected window size parameter value: " + windowParams); - } - } else { - maximize = false; - - winSizeW = DEFAULT_WINDOW_WIDTH; - winSizeH = DEFAULT_WINDOW_HEIGHT; - } - } - - private void invokeMain(Class mainClass) throws Exception { - Method method = mainClass.getMethod("main", String[].class); - - method.invoke(null, (Object) mcParams.toArray(new String[0])); - } - - private void legacyLaunch() throws Exception { - // Get the Minecraft Class and set the base folder - Class minecraftClass = classLoader.loadClass(mainClass); - - Field baseDirField = Utils.getMinecraftBaseDirField(minecraftClass); - - if (baseDirField == null) { - LOGGER.warning("Could not find Minecraft path field."); - } else { - baseDirField.setAccessible(true); - - baseDirField.set(null, new File(cwd)); - } - - System.setProperty("minecraft.applet.TargetDirectory", cwd); - - if (!traits.contains("noapplet")) { - LOGGER.info("Launching with applet wrapper..."); - - try { - Class mcAppletClass = classLoader.loadClass(appletClass); - - Applet mcApplet = (Applet) mcAppletClass.getConstructor().newInstance(); - - LegacyFrame mcWindow = new LegacyFrame(windowTitle, mcApplet); - - mcWindow.start( - userName, - sessionId, - winSizeW, - winSizeH, - maximize, - serverAddress, - serverPort, - mcParams.contains("--demo") - ); - - return; - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Applet wrapper failed: ", e); - - LOGGER.warning("Falling back to using main class."); - } - } - - invokeMain(minecraftClass); - } - - private void launchWithMainClass() throws Exception { - // window size, title and state, onesix - - // FIXME: there is no good way to maximize the minecraft window in onesix. - // the following often breaks linux screen setups - // mcparams.add("--fullscreen"); - - if (!maximize) { - mcParams.add("--width"); - mcParams.add(Integer.toString(winSizeW)); - mcParams.add("--height"); - mcParams.add(Integer.toString(winSizeH)); - } - - if (serverAddress != null) { - mcParams.add("--server"); - mcParams.add(serverAddress); - mcParams.add("--port"); - mcParams.add(serverPort); - } - - invokeMain(classLoader.loadClass(mainClass)); - } - - @Override - public void launch() throws Exception { - if (traits.contains("legacyLaunch") || traits.contains("alphaLaunch")) { - // legacy launch uses the applet wrapper - legacyLaunch(); - } else { - // normal launch just calls main() - launchWithMainClass(); - } - } - -} diff --git a/libraries/launcher/org/prismlauncher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/impl/StandardLauncher.java new file mode 100644 index 00000000..c651b060 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/impl/StandardLauncher.java @@ -0,0 +1,51 @@ +/* Copyright 2012-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.prismlauncher.impl; + +import org.prismlauncher.utils.Parameters; + +public final class StandardLauncher extends AbstractLauncher { + + public StandardLauncher(Parameters params) { + super(params); + } + + @Override + public void launch() throws Throwable { + // window size, title and state + + // FIXME: there is no good way to maximize the minecraft window from here. + // the following often breaks linux screen setups + // mcparams.add("--fullscreen"); + + if (!maximize) { + mcParams.add("--width"); + mcParams.add(Integer.toString(width)); + mcParams.add("--height"); + mcParams.add(Integer.toString(height)); + } + + if (serverAddress != null) { + mcParams.add("--server"); + mcParams.add(serverAddress); + mcParams.add("--port"); + mcParams.add(serverPort); + } + + loadAndInvokeMain(); + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 98a40c28..dcaba18d 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -25,22 +25,22 @@ import java.util.Map; public final class Parameters { - private final Map> paramsMap = new HashMap<>(); + private final Map> map = new HashMap<>(); public void add(String key, String value) { - List params = paramsMap.get(key); + List params = map.get(key); if (params == null) { params = new ArrayList<>(); - paramsMap.put(key, params); + map.put(key, params); } params.add(value); } public List all(String key) throws ParameterNotFoundException { - List params = paramsMap.get(key); + List params = map.get(key); if (params == null) throw new ParameterNotFoundException(key); @@ -49,7 +49,7 @@ public final class Parameters { } public List allSafe(String key, List def) { - List params = paramsMap.get(key); + List params = map.get(key); if (params == null || params.isEmpty()) return def; @@ -67,7 +67,7 @@ public final class Parameters { } public String firstSafe(String key, String def) { - List params = paramsMap.get(key); + List params = map.get(key); if (params == null || params.isEmpty()) return def; diff --git a/libraries/launcher/org/prismlauncher/utils/Utils.java b/libraries/launcher/org/prismlauncher/utils/Utils.java index ae9a4de2..79f5367b 100644 --- a/libraries/launcher/org/prismlauncher/utils/Utils.java +++ b/libraries/launcher/org/prismlauncher/utils/Utils.java @@ -29,7 +29,7 @@ public final class Utils { * * @param clazz the class to scan */ - public static Field getMinecraftBaseDirField(Class clazz) { + public static Field getMinecraftGameDirField(Class clazz) { for (Field f : clazz.getDeclaredFields()) { // Has to be File if (f.getType() != File.class) From 9062d28704f8508a031612f102c27a63b3655e0a Mon Sep 17 00:00:00 2001 From: solonovamax Date: Thu, 27 Oct 2022 18:01:17 -0400 Subject: [PATCH 006/277] Get rid of singleton, and refactor LauncherFactory to be a static class Signed-off-by: solonovamax --- .../org/prismlauncher/EntryPoint.java | 5 +-- .../org/prismlauncher/LauncherFactory.java | 31 ++++++++++--------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 73ff9753..37db6a5d 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -138,10 +138,7 @@ public final class EntryPoint { } try { - Launcher launcher = - LauncherFactory - .INSTANCE - .createLauncher(params); + Launcher launcher = LauncherFactory.createLauncher(params); launcher.launch(); diff --git a/libraries/launcher/org/prismlauncher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/LauncherFactory.java index 354ad1f0..5b18cc5f 100644 --- a/libraries/launcher/org/prismlauncher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/LauncherFactory.java @@ -35,6 +35,7 @@ package org.prismlauncher; + import org.prismlauncher.impl.LegacyLauncher; import org.prismlauncher.impl.StandardLauncher; import org.prismlauncher.utils.Parameters; @@ -42,13 +43,11 @@ import org.prismlauncher.utils.Parameters; import java.util.HashMap; import java.util.Map; + public final class LauncherFactory { - - public static final LauncherFactory INSTANCE = new LauncherFactory(); - - private final Map launcherRegistry = new HashMap<>(); - - private LauncherFactory() { + private static final Map launcherRegistry = new HashMap<>(); + + static { launcherRegistry.put("standard", new LauncherProvider() { @Override public Launcher provide(Parameters parameters) { @@ -62,22 +61,24 @@ public final class LauncherFactory { } }); } - - public Launcher createLauncher(Parameters parameters) { + private LauncherFactory() { + } + + public static Launcher createLauncher(Parameters parameters) { String name = parameters.first("launcher"); - + LauncherProvider launcherProvider = launcherRegistry.get(name); - + if (launcherProvider == null) throw new IllegalArgumentException("Invalid launcher type: " + name); - + return launcherProvider.provide(parameters); } - + public interface LauncherProvider { - + Launcher provide(Parameters parameters); - + } - + } From 107fa6b4f73c4b9178e5055995500fa9ad75da27 Mon Sep 17 00:00:00 2001 From: solonovamax Date: Thu, 27 Oct 2022 19:52:09 -0400 Subject: [PATCH 007/277] Code refactors - Refactor LauncherFactory.LauncherProvider to LauncherFactory - Refactor all launcher related components to launcher package - some basic code cleanup - Rename all, allSafe -> getList and first, firstSafe -> getString - Rename Utils -> LegacyUtils Signed-off-by: solonovamax --- libraries/launcher/CMakeLists.txt | 13 +- .../launcher/net/minecraft/Launcher.java | 182 +++++++++--------- .../org/prismlauncher/EntryPoint.java | 4 +- .../{ => launcher}/Launcher.java | 5 +- .../{ => launcher}/LauncherFactory.java | 17 +- .../launcher/LauncherProvider.java | 44 +++++ .../{ => launcher}/impl/AbstractLauncher.java | 43 +++-- .../{ => launcher}/impl/LegacyLauncher.java | 29 +-- .../{ => launcher}/impl/StandardLauncher.java | 2 +- .../utils/{Utils.java => LegacyUtils.java} | 27 +-- .../org/prismlauncher/utils/Parameters.java | 32 +-- 11 files changed, 227 insertions(+), 171 deletions(-) rename libraries/launcher/org/prismlauncher/{ => launcher}/Launcher.java (94%) rename libraries/launcher/org/prismlauncher/{ => launcher}/LauncherFactory.java (89%) create mode 100644 libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java rename libraries/launcher/org/prismlauncher/{ => launcher}/impl/AbstractLauncher.java (80%) rename libraries/launcher/org/prismlauncher/{ => launcher}/impl/LegacyLauncher.java (86%) rename libraries/launcher/org/prismlauncher/{ => launcher}/impl/StandardLauncher.java (97%) rename libraries/launcher/org/prismlauncher/utils/{Utils.java => LegacyUtils.java} (71%) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index ee88d0f9..d74c3641 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -8,16 +8,17 @@ set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unche set(SRC org/prismlauncher/EntryPoint.java - org/prismlauncher/Launcher.java - org/prismlauncher/LauncherFactory.java - org/prismlauncher/impl/AbstractLauncher.java - org/prismlauncher/impl/LegacyLauncher.java - org/prismlauncher/impl/StandardLauncher.java + org/prismlauncher/launcher/Launcher.java + org/prismlauncher/launcher/LauncherFactory.java + org/prismlauncher/launcher/LauncherProvider.java + org/prismlauncher/launcher/impl/AbstractLauncher.java + org/prismlauncher/launcher/impl/LegacyLauncher.java + org/prismlauncher/launcher/impl/StandardLauncher.java org/prismlauncher/applet/LegacyFrame.java org/prismlauncher/exception/ParameterNotFoundException.java org/prismlauncher/exception/ParseException.java org/prismlauncher/utils/Parameters.java - org/prismlauncher/utils/Utils.java + org/prismlauncher/utils/LegacyUtils.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 6bf671be..f9fd0a97 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -16,157 +16,167 @@ package net.minecraft; + import java.applet.Applet; import java.applet.AppletStub; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Graphics; import java.net.MalformedURLException; import java.net.URL; import java.util.Map; import java.util.TreeMap; + /* * WARNING: This class is reflectively accessed by legacy Forge versions. * Changing field and method declarations without further testing is not recommended. */ public final class Launcher extends Applet implements AppletStub { - + private final Map params = new TreeMap<>(); - + private Applet wrappedApplet; - - private URL documentBase; - + + private final URL documentBase; + private boolean active = false; - + public Launcher(Applet applet) { this(applet, null); } - + public Launcher(Applet applet, URL documentBase) { this.setLayout(new BorderLayout()); - + this.add(applet, "Center"); - + this.wrappedApplet = applet; - + try { if (documentBase != null) { this.documentBase = documentBase; } else if (applet.getClass().getPackage().getName().startsWith("com.mojang")) { // Special case only for Classic versions - + + // TODO: 2022-10-27 Can this be changed to https this.documentBase = new URL("http", "www.minecraft.net", 80, "/game/"); } else { + // TODO: 2022-10-27 Can this be changed to https? this.documentBase = new URL("http://www.minecraft.net/game/"); } } catch (MalformedURLException e) { throw new RuntimeException(e); } } - + public void replace(Applet applet) { this.wrappedApplet = applet; - + applet.setStub(this); applet.setSize(getWidth(), getHeight()); - + this.setLayout(new BorderLayout()); this.add(applet, "Center"); - + applet.init(); - + active = true; - + applet.start(); - + validate(); } - - public void setParameter(String name, String value) { - params.put(name, value); - } - - @Override - public String getParameter(String name) { - String param = params.get(name); - - if (param != null) - return param; - - try { - return super.getParameter(name); - } catch (Exception ignored) {} - - return null; - } - + @Override public boolean isActive() { return active; } - + @Override - public void appletResize(int width, int height) { - wrappedApplet.resize(width, height); + public URL getDocumentBase() { + return documentBase; } - - @Override - public void resize(int width, int height) { - wrappedApplet.resize(width, height); - } - - @Override - public void resize(Dimension d) { - wrappedApplet.resize(d); - } - - @Override - public void init() { - if (wrappedApplet != null) - wrappedApplet.init(); - } - - @Override - public void start() { - wrappedApplet.start(); - - active = true; - } - - @Override - public void stop() { - wrappedApplet.stop(); - - active = false; - } - - public void destroy() { - wrappedApplet.destroy(); - } - + @Override public URL getCodeBase() { try { + // TODO: 2022-10-27 Can this be changed to https? return new URL("http://www.minecraft.net/game/"); } catch (MalformedURLException e) { throw new RuntimeException(e); } } - + @Override - public URL getDocumentBase() { - return documentBase; + public String getParameter(String name) { + String param = params.get(name); + + if (param != null) + return param; + + try { + return super.getParameter(name); + } catch (Exception ignored) { + } + + return null; } - + + @Override + public void resize(int width, int height) { + wrappedApplet.resize(width, height); + } + + @Override + public void resize(Dimension d) { + wrappedApplet.resize(d); + } + + @Override + public void init() { + if (wrappedApplet != null) + wrappedApplet.init(); + } + + @Override + public void start() { + wrappedApplet.start(); + + active = true; + } + + @Override + public void stop() { + wrappedApplet.stop(); + + active = false; + } + + public void destroy() { + wrappedApplet.destroy(); + } + + @Override + public void appletResize(int width, int height) { + wrappedApplet.resize(width, height); + } + @Override public void setVisible(boolean b) { super.setVisible(b); - + wrappedApplet.setVisible(b); } - - public void update(Graphics paramGraphics) {} - - public void paint(Graphics paramGraphics) {} - + + public void paint(Graphics paramGraphics) { + } + + public void update(Graphics paramGraphics) { + } + + public void setParameter(String name, String value) { + params.put(name, value); + } + } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 37db6a5d..44e947b1 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -53,6 +53,8 @@ package org.prismlauncher; import org.prismlauncher.exception.ParseException; +import org.prismlauncher.launcher.Launcher; +import org.prismlauncher.launcher.LauncherFactory; import org.prismlauncher.utils.Parameters; import java.io.BufferedReader; @@ -81,7 +83,7 @@ public final class EntryPoint { } private Action parseLine(String inData) throws ParseException { - if (inData.length() == 0) + if (inData.isEmpty()) throw new ParseException("Unexpected empty string!"); String first = inData; diff --git a/libraries/launcher/org/prismlauncher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java similarity index 94% rename from libraries/launcher/org/prismlauncher/Launcher.java rename to libraries/launcher/org/prismlauncher/launcher/Launcher.java index 50c2c9c8..1cea255c 100644 --- a/libraries/launcher/org/prismlauncher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package org.prismlauncher; +package org.prismlauncher.launcher; + public interface Launcher { - void launch() throws Throwable; - } diff --git a/libraries/launcher/org/prismlauncher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java similarity index 89% rename from libraries/launcher/org/prismlauncher/LauncherFactory.java rename to libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index 5b18cc5f..6c601171 100644 --- a/libraries/launcher/org/prismlauncher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea, * * This program is free software: you can redistribute it and/or modify @@ -33,11 +33,11 @@ * along with this program. If not, see . */ -package org.prismlauncher; +package org.prismlauncher.launcher; -import org.prismlauncher.impl.LegacyLauncher; -import org.prismlauncher.impl.StandardLauncher; +import org.prismlauncher.launcher.impl.LegacyLauncher; +import org.prismlauncher.launcher.impl.StandardLauncher; import org.prismlauncher.utils.Parameters; import java.util.HashMap; @@ -65,7 +65,7 @@ public final class LauncherFactory { } public static Launcher createLauncher(Parameters parameters) { - String name = parameters.first("launcher"); + String name = parameters.getString("launcher"); LauncherProvider launcherProvider = launcherRegistry.get(name); @@ -74,11 +74,4 @@ public final class LauncherFactory { return launcherProvider.provide(parameters); } - - public interface LauncherProvider { - - Launcher provide(Parameters parameters); - - } - } diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java new file mode 100644 index 00000000..b6d1caab --- /dev/null +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 solonovamax, + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.launcher; + + +import org.prismlauncher.utils.Parameters; + + +public interface LauncherProvider { + Launcher provide(Parameters parameters); +} diff --git a/libraries/launcher/org/prismlauncher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java similarity index 80% rename from libraries/launcher/org/prismlauncher/impl/AbstractLauncher.java rename to libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 49a984f5..637c5da7 100644 --- a/libraries/launcher/org/prismlauncher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -13,21 +13,22 @@ * limitations under the License. */ -package org.prismlauncher.impl; +package org.prismlauncher.launcher.impl; + + +import org.prismlauncher.exception.ParseException; +import org.prismlauncher.launcher.Launcher; +import org.prismlauncher.utils.Parameters; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import org.prismlauncher.Launcher; -import org.prismlauncher.exception.ParseException; -import org.prismlauncher.utils.Parameters; public abstract class AbstractLauncher implements Launcher { - + private static final int DEFAULT_WINDOW_WIDTH = 854; private static final int DEFAULT_WINDOW_HEIGHT = 480; @@ -43,21 +44,21 @@ public abstract class AbstractLauncher implements Launcher { protected final String serverAddress, serverPort; protected final ClassLoader classLoader; - - public AbstractLauncher(Parameters params) { + + protected AbstractLauncher(Parameters params) { classLoader = ClassLoader.getSystemClassLoader(); - - mcParams = params.allSafe("param", new ArrayList()); - mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); - - serverAddress = params.firstSafe("serverAddress", null); - serverPort = params.firstSafe("serverPort", null); - - String windowParams = params.firstSafe("windowParams", null); - + + mcParams = params.getList("param", new ArrayList()); + mainClass = params.getString("mainClass", "net.minecraft.client.Minecraft"); + + serverAddress = params.getString("serverAddress", null); + serverPort = params.getString("serverPort", null); + + String windowParams = params.getString("windowParams", null); + if ("max".equals(windowParams) || windowParams == null) { maximize = windowParams != null; - + width = DEFAULT_WINDOW_WIDTH; height = DEFAULT_WINDOW_HEIGHT; } else { @@ -70,7 +71,7 @@ public abstract class AbstractLauncher implements Launcher { width = Integer.parseInt(windowParams.substring(0, byIndex)); height = Integer.parseInt(windowParams.substring(byIndex + 1)); return; - } catch(NumberFormatException pass) { + } catch (NumberFormatException ignored) { } } @@ -81,8 +82,8 @@ public abstract class AbstractLauncher implements Launcher { protected Class loadMain() throws ClassNotFoundException { return classLoader.loadClass(mainClass); } - - protected void loadAndInvokeMain() throws Throwable, ClassNotFoundException { + + protected void loadAndInvokeMain() throws Throwable { invokeMain(loadMain()); } diff --git a/libraries/launcher/org/prismlauncher/impl/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java similarity index 86% rename from libraries/launcher/org/prismlauncher/impl/LegacyLauncher.java rename to libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java index 30a4dba7..181156c6 100644 --- a/libraries/launcher/org/prismlauncher/impl/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java @@ -13,7 +13,12 @@ * limitations under the License. */ -package org.prismlauncher.impl; +package org.prismlauncher.launcher.impl; + + +import org.prismlauncher.applet.LegacyFrame; +import org.prismlauncher.utils.LegacyUtils; +import org.prismlauncher.utils.Parameters; import java.applet.Applet; import java.io.File; @@ -26,10 +31,6 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import org.prismlauncher.applet.LegacyFrame; -import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.Utils; - @SuppressWarnings("removal") public final class LegacyLauncher extends AbstractLauncher { @@ -44,22 +45,22 @@ public final class LegacyLauncher extends AbstractLauncher { public LegacyLauncher(Parameters params) { super(params); - - user = params.first("userName"); - session = params.first("sessionId"); - title = params.firstSafe("windowTitle", "Minecraft"); - appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); - - List traits = params.allSafe("traits", Collections.emptyList()); + + user = params.getString("userName"); + session = params.getString("sessionId"); + title = params.getString("windowTitle", "Minecraft"); + appletClass = params.getString("appletClass", "net.minecraft.client.MinecraftApplet"); + + List traits = params.getList("traits", Collections.emptyList()); noApplet = traits.contains("noapplet"); - + cwd = System.getProperty("user.dir"); } @Override public void launch() throws Throwable { Class main = loadMain(); - Field gameDirField = Utils.getMinecraftGameDirField(main); + Field gameDirField = LegacyUtils.getMinecraftGameDirField(main); if (gameDirField == null) { LOGGER.warning("Could not find Mineraft path field."); diff --git a/libraries/launcher/org/prismlauncher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java similarity index 97% rename from libraries/launcher/org/prismlauncher/impl/StandardLauncher.java rename to libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index c651b060..c1d33958 100644 --- a/libraries/launcher/org/prismlauncher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -13,7 +13,7 @@ * limitations under the License. */ -package org.prismlauncher.impl; +package org.prismlauncher.launcher.impl; import org.prismlauncher.utils.Parameters; diff --git a/libraries/launcher/org/prismlauncher/utils/Utils.java b/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java similarity index 71% rename from libraries/launcher/org/prismlauncher/utils/Utils.java rename to libraries/launcher/org/prismlauncher/utils/LegacyUtils.java index 79f5367b..7607d731 100644 --- a/libraries/launcher/org/prismlauncher/utils/Utils.java +++ b/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java @@ -16,34 +16,39 @@ package org.prismlauncher.utils; + import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -public final class Utils { - - private Utils() {} +public final class LegacyUtils { + + private LegacyUtils() { + } + /** * Finds a field that looks like a Minecraft base folder in a supplied class * * @param clazz the class to scan */ public static Field getMinecraftGameDirField(Class clazz) { - for (Field f : clazz.getDeclaredFields()) { + // Field we're looking for is always + // private static File obfuscatedName = null; + for (Field field : clazz.getDeclaredFields()) { // Has to be File - if (f.getType() != File.class) + if (field.getType() != File.class) continue; - + // And Private Static. - if (!Modifier.isStatic(f.getModifiers()) || !Modifier.isPrivate(f.getModifiers())) + if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers())) continue; - - return f; + + return field; } - + return null; } - + } diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index dcaba18d..00b0701f 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -39,39 +39,39 @@ public final class Parameters { params.add(value); } - public List all(String key) throws ParameterNotFoundException { + public List getList(String key) throws ParameterNotFoundException { List params = map.get(key); - + if (params == null) throw new ParameterNotFoundException(key); - + return params; } - - public List allSafe(String key, List def) { + + public List getList(String key, List def) { List params = map.get(key); - + if (params == null || params.isEmpty()) return def; - + return params; } - - public String first(String key) throws ParameterNotFoundException { - List list = all(key); - + + public String getString(String key) throws ParameterNotFoundException { + List list = getList(key); + if (list.isEmpty()) throw new ParameterNotFoundException(key); - + return list.get(0); } - - public String firstSafe(String key, String def) { + + public String getString(String key, String def) { List params = map.get(key); - + if (params == null || params.isEmpty()) return def; - + return params.get(0); } From 99d9868116c14418cdcf5c033be7eed0d0cffd98 Mon Sep 17 00:00:00 2001 From: solonovamax Date: Thu, 27 Oct 2022 20:12:39 -0400 Subject: [PATCH 008/277] Cleanup EntryPoint code - Don't return an int from listen(). An enum is preferred. - Make parseLine() static, and pass Parameters to it. Signed-off-by: solonovamax --- .../org/prismlauncher/EntryPoint.java | 103 ++++++++++-------- 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 44e947b1..b1464f59 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -52,6 +52,7 @@ package org.prismlauncher; + import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherFactory; @@ -64,102 +65,114 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Level; import java.util.logging.Logger; + public final class EntryPoint { - private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - + private final Parameters params = new Parameters(); - + public static void main(String[] args) { EntryPoint listener = new EntryPoint(); - - int retCode = listener.listen(); - - if (retCode != 0) { - LOGGER.info("Exiting with " + retCode); - - System.exit(retCode); + + ExitCode exitCode = listener.listen(); + + if (exitCode != ExitCode.NORMAL) { + LOGGER.warning("Exiting with " + exitCode); + + System.exit(exitCode.numericalCode); } } - - private Action parseLine(String inData) throws ParseException { + + private static PreLaunchAction parseLine(String inData, Parameters params) throws ParseException { if (inData.isEmpty()) throw new ParseException("Unexpected empty string!"); - + String first = inData; String second = null; int splitPoint = inData.indexOf(' '); - + if (splitPoint != -1) { first = first.substring(0, splitPoint); second = inData.substring(splitPoint + 1); } - + switch (first) { case "launch": - return Action.LAUNCH; + return PreLaunchAction.LAUNCH; case "abort": - return Action.ABORT; + return PreLaunchAction.ABORT; default: if (second == null || second.isEmpty()) throw new ParseException("Error while parsing:" + inData); - + params.add(first, second); - - return Action.PROCEED; + + return PreLaunchAction.PROCEED; } } - - public int listen() { - Action action = Action.PROCEED; - + + public ExitCode listen() { + PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; + try (BufferedReader reader = new BufferedReader(new InputStreamReader( System.in, StandardCharsets.UTF_8 ))) { String line; - - while (action == Action.PROCEED) { + + while (preLaunchAction == PreLaunchAction.PROCEED) { if ((line = reader.readLine()) != null) { - action = parseLine(line); + preLaunchAction = parseLine(line, this.params); } else { - action = Action.ABORT; + preLaunchAction = PreLaunchAction.ABORT; } } } catch (IOException | ParseException e) { LOGGER.log(Level.SEVERE, "Launcher abort due to exception:", e); - - return 1; + + return ExitCode.ERROR; } - + // Main loop - if (action == Action.ABORT) { + if (preLaunchAction == PreLaunchAction.ABORT) { LOGGER.info("Launch aborted by the launcher."); - - return 1; + + return ExitCode.ERROR; } - + try { Launcher launcher = LauncherFactory.createLauncher(params); - + launcher.launch(); - - return 0; + + return ExitCode.NORMAL; } catch (IllegalArgumentException e) { LOGGER.log(Level.SEVERE, "Wrong argument.", e); - - return 1; + + return ExitCode.ERROR; } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); - - return 1; + + return ExitCode.ERROR; } } - - private enum Action { + + private enum PreLaunchAction { PROCEED, LAUNCH, ABORT } - + + + private enum ExitCode { + NORMAL(0), + ERROR(1); + + private final int numericalCode; + + ExitCode(int numericalCode) { + this.numericalCode = numericalCode; + } + } + } From e5622ce824cf94074ced947ed7277886b2756ba9 Mon Sep 17 00:00:00 2001 From: solonovamax Date: Thu, 27 Oct 2022 20:16:08 -0400 Subject: [PATCH 009/277] Code cleanup to net.minecraft.Launcher No code logic has been changed, only: - add @Override annotatons - change setVisible(boolean b) -> setVisible(boolean visible) - Change block commend on class -> javadoc comment Signed-off-by: solonovamax --- libraries/launcher/net/minecraft/Launcher.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index f9fd0a97..8928dbbe 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -28,8 +28,9 @@ import java.util.Map; import java.util.TreeMap; -/* +/** * WARNING: This class is reflectively accessed by legacy Forge versions. + *

* Changing field and method declarations without further testing is not recommended. */ public final class Launcher extends Applet implements AppletStub { @@ -47,6 +48,7 @@ public final class Launcher extends Applet implements AppletStub { } public Launcher(Applet applet, URL documentBase) { + super(); this.setLayout(new BorderLayout()); this.add(applet, "Center"); @@ -153,6 +155,7 @@ public final class Launcher extends Applet implements AppletStub { active = false; } + @Override public void destroy() { wrappedApplet.destroy(); } @@ -163,15 +166,17 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public void setVisible(boolean b) { - super.setVisible(b); + public void setVisible(boolean visible) { + super.setVisible(visible); - wrappedApplet.setVisible(b); + wrappedApplet.setVisible(visible); } + @Override public void paint(Graphics paramGraphics) { } + @Override public void update(Graphics paramGraphics) { } From e86fbc697f819345cd42db7008509e89cfdddd73 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 29 Oct 2022 09:47:09 +0100 Subject: [PATCH 010/277] Fix formatting Signed-off-by: TheKodeToad --- .../launcher/net/minecraft/Launcher.java | 80 +++++++++---------- .../org/prismlauncher/EntryPoint.java | 58 +++++++------- .../org/prismlauncher/launcher/Launcher.java | 3 +- .../launcher/LauncherFactory.java | 13 ++- .../launcher/LauncherProvider.java | 4 +- .../launcher/impl/AbstractLauncher.java | 18 ++--- .../launcher/impl/LegacyLauncher.java | 7 +- .../org/prismlauncher/utils/LegacyUtils.java | 14 ++-- 8 files changed, 94 insertions(+), 103 deletions(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 8928dbbe..77050d90 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -16,7 +16,6 @@ package net.minecraft; - import java.applet.Applet; import java.applet.AppletStub; import java.awt.BorderLayout; @@ -27,40 +26,39 @@ import java.net.URL; import java.util.Map; import java.util.TreeMap; - /** * WARNING: This class is reflectively accessed by legacy Forge versions. *

* Changing field and method declarations without further testing is not recommended. */ public final class Launcher extends Applet implements AppletStub { - + private final Map params = new TreeMap<>(); - + private Applet wrappedApplet; - + private final URL documentBase; - + private boolean active = false; - + public Launcher(Applet applet) { this(applet, null); } - + public Launcher(Applet applet, URL documentBase) { super(); this.setLayout(new BorderLayout()); - + this.add(applet, "Center"); - + this.wrappedApplet = applet; - + try { if (documentBase != null) { this.documentBase = documentBase; } else if (applet.getClass().getPackage().getName().startsWith("com.mojang")) { // Special case only for Classic versions - + // TODO: 2022-10-27 Can this be changed to https this.documentBase = new URL("http", "www.minecraft.net", 80, "/game/"); } else { @@ -71,35 +69,35 @@ public final class Launcher extends Applet implements AppletStub { throw new RuntimeException(e); } } - + public void replace(Applet applet) { this.wrappedApplet = applet; - + applet.setStub(this); applet.setSize(getWidth(), getHeight()); - + this.setLayout(new BorderLayout()); this.add(applet, "Center"); - + applet.init(); - + active = true; - + applet.start(); - + validate(); } - + @Override public boolean isActive() { return active; } - + @Override public URL getDocumentBase() { return documentBase; } - + @Override public URL getCodeBase() { try { @@ -109,79 +107,79 @@ public final class Launcher extends Applet implements AppletStub { throw new RuntimeException(e); } } - + @Override public String getParameter(String name) { String param = params.get(name); - + if (param != null) return param; - + try { return super.getParameter(name); } catch (Exception ignored) { } - + return null; } - + @Override public void resize(int width, int height) { wrappedApplet.resize(width, height); } - + @Override public void resize(Dimension d) { wrappedApplet.resize(d); } - + @Override public void init() { if (wrappedApplet != null) wrappedApplet.init(); } - + @Override public void start() { wrappedApplet.start(); - + active = true; } - + @Override public void stop() { wrappedApplet.stop(); - + active = false; } - + @Override public void destroy() { wrappedApplet.destroy(); } - + @Override public void appletResize(int width, int height) { wrappedApplet.resize(width, height); } - + @Override public void setVisible(boolean visible) { super.setVisible(visible); - + wrappedApplet.setVisible(visible); } - + @Override public void paint(Graphics paramGraphics) { } - + @Override public void update(Graphics paramGraphics) { } - + public void setParameter(String name, String value) { params.put(name, value); } - + } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index b1464f59..cb68b8fd 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -52,7 +52,6 @@ package org.prismlauncher; - import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherFactory; @@ -65,37 +64,37 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Level; import java.util.logging.Logger; - public final class EntryPoint { + private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - + private final Parameters params = new Parameters(); - + public static void main(String[] args) { EntryPoint listener = new EntryPoint(); - + ExitCode exitCode = listener.listen(); - + if (exitCode != ExitCode.NORMAL) { LOGGER.warning("Exiting with " + exitCode); - + System.exit(exitCode.numericalCode); } } - + private static PreLaunchAction parseLine(String inData, Parameters params) throws ParseException { if (inData.isEmpty()) throw new ParseException("Unexpected empty string!"); - + String first = inData; String second = null; int splitPoint = inData.indexOf(' '); - + if (splitPoint != -1) { first = first.substring(0, splitPoint); second = inData.substring(splitPoint + 1); } - + switch (first) { case "launch": return PreLaunchAction.LAUNCH; @@ -104,22 +103,22 @@ public final class EntryPoint { default: if (second == null || second.isEmpty()) throw new ParseException("Error while parsing:" + inData); - + params.add(first, second); - + return PreLaunchAction.PROCEED; } } - + public ExitCode listen() { PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; - + try (BufferedReader reader = new BufferedReader(new InputStreamReader( System.in, StandardCharsets.UTF_8 ))) { String line; - + while (preLaunchAction == PreLaunchAction.PROCEED) { if ((line = reader.readLine()) != null) { preLaunchAction = parseLine(line, this.params); @@ -129,50 +128,49 @@ public final class EntryPoint { } } catch (IOException | ParseException e) { LOGGER.log(Level.SEVERE, "Launcher abort due to exception:", e); - + return ExitCode.ERROR; } - + // Main loop if (preLaunchAction == PreLaunchAction.ABORT) { LOGGER.info("Launch aborted by the launcher."); - + return ExitCode.ERROR; } - + try { Launcher launcher = LauncherFactory.createLauncher(params); - + launcher.launch(); - + return ExitCode.NORMAL; } catch (IllegalArgumentException e) { LOGGER.log(Level.SEVERE, "Wrong argument.", e); - + return ExitCode.ERROR; } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); - + return ExitCode.ERROR; } } - + private enum PreLaunchAction { PROCEED, LAUNCH, ABORT } - - + private enum ExitCode { NORMAL(0), ERROR(1); - + private final int numericalCode; - + ExitCode(int numericalCode) { this.numericalCode = numericalCode; } } - + } diff --git a/libraries/launcher/org/prismlauncher/launcher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java index 1cea255c..6f5c17b8 100644 --- a/libraries/launcher/org/prismlauncher/launcher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -16,7 +16,8 @@ package org.prismlauncher.launcher; - public interface Launcher { + void launch() throws Throwable; + } diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index 6c601171..761a4595 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -35,7 +35,6 @@ package org.prismlauncher.launcher; - import org.prismlauncher.launcher.impl.LegacyLauncher; import org.prismlauncher.launcher.impl.StandardLauncher; import org.prismlauncher.utils.Parameters; @@ -43,10 +42,10 @@ import org.prismlauncher.utils.Parameters; import java.util.HashMap; import java.util.Map; - public final class LauncherFactory { + private static final Map launcherRegistry = new HashMap<>(); - + static { launcherRegistry.put("standard", new LauncherProvider() { @Override @@ -63,15 +62,15 @@ public final class LauncherFactory { } private LauncherFactory() { } - + public static Launcher createLauncher(Parameters parameters) { String name = parameters.getString("launcher"); - + LauncherProvider launcherProvider = launcherRegistry.get(name); - + if (launcherProvider == null) throw new IllegalArgumentException("Invalid launcher type: " + name); - + return launcherProvider.provide(parameters); } } diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java index b6d1caab..9b453c7b 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java @@ -35,10 +35,10 @@ package org.prismlauncher.launcher; - import org.prismlauncher.utils.Parameters; - public interface LauncherProvider { + Launcher provide(Parameters parameters); + } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 637c5da7..5aab40ff 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -15,7 +15,6 @@ package org.prismlauncher.launcher.impl; - import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.utils.Parameters; @@ -26,9 +25,8 @@ import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.List; - public abstract class AbstractLauncher implements Launcher { - + private static final int DEFAULT_WINDOW_WIDTH = 854; private static final int DEFAULT_WINDOW_HEIGHT = 480; @@ -44,21 +42,21 @@ public abstract class AbstractLauncher implements Launcher { protected final String serverAddress, serverPort; protected final ClassLoader classLoader; - + protected AbstractLauncher(Parameters params) { classLoader = ClassLoader.getSystemClassLoader(); - + mcParams = params.getList("param", new ArrayList()); mainClass = params.getString("mainClass", "net.minecraft.client.Minecraft"); - + serverAddress = params.getString("serverAddress", null); serverPort = params.getString("serverPort", null); - + String windowParams = params.getString("windowParams", null); - + if ("max".equals(windowParams) || windowParams == null) { maximize = windowParams != null; - + width = DEFAULT_WINDOW_WIDTH; height = DEFAULT_WINDOW_HEIGHT; } else { @@ -82,7 +80,7 @@ public abstract class AbstractLauncher implements Launcher { protected Class loadMain() throws ClassNotFoundException { return classLoader.loadClass(mainClass); } - + protected void loadAndInvokeMain() throws Throwable { invokeMain(loadMain()); } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java index 181156c6..57e29605 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java @@ -15,7 +15,6 @@ package org.prismlauncher.launcher.impl; - import org.prismlauncher.applet.LegacyFrame; import org.prismlauncher.utils.LegacyUtils; import org.prismlauncher.utils.Parameters; @@ -45,15 +44,15 @@ public final class LegacyLauncher extends AbstractLauncher { public LegacyLauncher(Parameters params) { super(params); - + user = params.getString("userName"); session = params.getString("sessionId"); title = params.getString("windowTitle", "Minecraft"); appletClass = params.getString("appletClass", "net.minecraft.client.MinecraftApplet"); - + List traits = params.getList("traits", Collections.emptyList()); noApplet = traits.contains("noapplet"); - + cwd = System.getProperty("user.dir"); } diff --git a/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java b/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java index 7607d731..ca648012 100644 --- a/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java @@ -16,17 +16,15 @@ package org.prismlauncher.utils; - import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; - public final class LegacyUtils { - + private LegacyUtils() { } - + /** * Finds a field that looks like a Minecraft base folder in a supplied class * @@ -39,16 +37,16 @@ public final class LegacyUtils { // Has to be File if (field.getType() != File.class) continue; - + // And Private Static. if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers())) continue; - + return field; } - + return null; } - + } From c0b8c53e6960de48c6a43796b413d1780f681a3a Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 29 Oct 2022 15:05:43 +0100 Subject: [PATCH 011/277] Attempt to sort out copyright headers Signed-off-by: TheKodeToad --- .../launcher/net/minecraft/Launcher.java | 58 ++++++++++--- .../org/prismlauncher/EntryPoint.java | 4 +- .../org/prismlauncher/applet/LegacyFrame.java | 60 +++++++++++--- .../exception/ParameterNotFoundException.java | 58 ++++++++++--- .../exception/ParseException.java | 58 ++++++++++--- .../org/prismlauncher/launcher/Launcher.java | 61 +++++++++++--- .../launcher/LauncherFactory.java | 26 +++++- .../launcher/LauncherProvider.java | 20 ++++- .../launcher/impl/AbstractLauncher.java | 60 +++++++++++--- .../launcher/impl/LegacyLauncher.java | 60 +++++++++++--- .../launcher/impl/StandardLauncher.java | 59 ++++++++++--- .../org/prismlauncher/utils/LegacyUtils.java | 58 ++++++++++--- .../org/prismlauncher/utils/Parameters.java | 82 ++++++++++++++----- 13 files changed, 548 insertions(+), 116 deletions(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 77050d90..2f8a51c9 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -1,17 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package net.minecraft; diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index cb68b8fd..40494b7c 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher - * Copyright (C) 2022 icelimetea, + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * * 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 diff --git a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java b/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java index f3359fca..20ec2503 100644 --- a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java @@ -1,17 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 flow + * Copyright (C) 2022 Samisafool + * Copyright (C) 2022 TheKodeToad * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.applet; diff --git a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java index 641e0c99..c083e02a 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java @@ -1,17 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Samisafool * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.exception; diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 51d25a62..8904f9ee 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -1,17 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Samisafool * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.exception; diff --git a/libraries/launcher/org/prismlauncher/launcher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java index 6f5c17b8..a41e4af6 100644 --- a/libraries/launcher/org/prismlauncher/launcher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -1,19 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Samisafool + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package org.prismlauncher.launcher; public interface Launcher { diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index 761a4595..e6de35fa 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -1,7 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 icelimetea, + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Samisafool + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * * 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 @@ -31,6 +35,23 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.launcher; @@ -73,4 +94,5 @@ public final class LauncherFactory { return launcherProvider.provide(parameters); } + } diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java index 9b453c7b..66d23c0f 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher - * Copyright (C) 2022 solonovamax, + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 solonovamax * * 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 @@ -31,6 +32,23 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.launcher; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 5aab40ff..7a622b1b 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -1,16 +1,56 @@ -/* Copyright 2012-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.launcher.impl; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java index 57e29605..fb1540bf 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java @@ -1,16 +1,56 @@ -/* Copyright 2012-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 flow + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.launcher.impl; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index c1d33958..5c884e99 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -1,16 +1,55 @@ -/* Copyright 2012-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.launcher.impl; diff --git a/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java b/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java index ca648012..8e7cbb74 100644 --- a/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java @@ -1,17 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.utils; diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 00b0701f..5596e88a 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -1,17 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-only /* - * Copyright 2012-2021 MultiMC Contributors + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 icelimetea + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 Samisafool + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 solonovamax * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.utils; @@ -41,37 +81,37 @@ public final class Parameters { public List getList(String key) throws ParameterNotFoundException { List params = map.get(key); - + if (params == null) throw new ParameterNotFoundException(key); - + return params; } - + public List getList(String key, List def) { List params = map.get(key); - + if (params == null || params.isEmpty()) return def; - + return params; } - + public String getString(String key) throws ParameterNotFoundException { List list = getList(key); - + if (list.isEmpty()) throw new ParameterNotFoundException(key); - + return list.get(0); } - + public String getString(String key, String def) { List params = map.get(key); - + if (params == null || params.isEmpty()) return def; - + return params.get(0); } From a7b1700d4232cc055c50286a57d627db23d6b4cd Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 29 Oct 2022 15:20:27 +0100 Subject: [PATCH 012/277] Better variable naming Signed-off-by: TheKodeToad --- .../launcher/net/minecraft/Launcher.java | 9 ++- .../org/prismlauncher/applet/LegacyFrame.java | 55 +++++++++---------- .../org/prismlauncher/launcher/Launcher.java | 1 + .../launcher/impl/LegacyLauncher.java | 4 +- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 2f8a51c9..aef09ca9 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -84,7 +84,6 @@ public final class Launcher extends Applet implements AppletStub { } public Launcher(Applet applet, URL documentBase) { - super(); this.setLayout(new BorderLayout()); this.add(applet, "Center"); @@ -167,8 +166,8 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public void resize(Dimension d) { - wrappedApplet.resize(d); + public void resize(Dimension size) { + wrappedApplet.resize(size); } @Override @@ -209,11 +208,11 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public void paint(Graphics paramGraphics) { + public void paint(Graphics graphics) { } @Override - public void update(Graphics paramGraphics) { + public void update(Graphics graphics) { } public void setParameter(String name, String value) { diff --git a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java b/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java index 20ec2503..58125160 100644 --- a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java @@ -74,19 +74,18 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -@SuppressWarnings("removal") public final class LegacyFrame extends Frame { private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); - private final Launcher appletWrap; + private final Launcher launcher; - public LegacyFrame(String title, Applet mcApplet) { + public LegacyFrame(String title, Applet applet) { super(title); - appletWrap = new Launcher(mcApplet); + launcher = new Launcher(applet); - mcApplet.setStub(appletWrap); + applet.setStub(launcher); try { setIconImage(ImageIO.read(new File("icon.png"))); @@ -100,8 +99,8 @@ public final class LegacyFrame extends Frame { public void start ( String user, String session, - int winSizeW, - int winSizeH, + int width, + int height, boolean maximize, String serverAddress, String serverPort, @@ -130,9 +129,9 @@ public final class LegacyFrame extends Frame { LOGGER.warning("Mpticket file is corrupted!"); } else { // Assumes parameters are valid and in the correct order - appletWrap.setParameter("server", lines.get(0)); - appletWrap.setParameter("port", lines.get(1)); - appletWrap.setParameter("mppass", lines.get(2)); + launcher.setParameter("server", lines.get(0)); + launcher.setParameter("port", lines.get(1)); + launcher.setParameter("mppass", lines.get(2)); } } catch (IOException e) { LOGGER.log(Level.WARNING, "Unable to read mpticket file!", e); @@ -140,20 +139,20 @@ public final class LegacyFrame extends Frame { } if (serverAddress != null) { - appletWrap.setParameter("server", serverAddress); - appletWrap.setParameter("port", serverPort); + launcher.setParameter("server", serverAddress); + launcher.setParameter("port", serverPort); } - appletWrap.setParameter("username", user); - appletWrap.setParameter("sessionid", session); - appletWrap.setParameter("stand-alone", "true"); // Show the quit button. TODO: why won't this work? - appletWrap.setParameter("haspaid", "true"); // Some old versions need this for world saves to work. - appletWrap.setParameter("demo", isDemo ? "true" : "false"); - appletWrap.setParameter("fullscreen", "false"); + launcher.setParameter("username", user); + launcher.setParameter("sessionid", session); + launcher.setParameter("stand-alone", "true"); // Show the quit button. TODO: why won't this work? + launcher.setParameter("haspaid", "true"); // Some old versions need this for world saves to work. + launcher.setParameter("demo", isDemo ? "true" : "false"); + launcher.setParameter("fullscreen", "false"); - add(appletWrap); + add(launcher); - appletWrap.setPreferredSize(new Dimension(winSizeW, winSizeH)); + launcher.setPreferredSize(new Dimension(width, height)); pack(); @@ -165,8 +164,8 @@ public final class LegacyFrame extends Frame { validate(); - appletWrap.init(); - appletWrap.start(); + launcher.init(); + launcher.start(); setVisible(true); } @@ -174,14 +173,14 @@ public final class LegacyFrame extends Frame { private final class ForceExitHandler extends WindowAdapter { @Override - public void windowClosing(WindowEvent e) { + public void windowClosing(WindowEvent event) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(30000L); - } catch (InterruptedException localInterruptedException) { - localInterruptedException.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); } LOGGER.info("Forcing exit!"); @@ -190,9 +189,9 @@ public final class LegacyFrame extends Frame { } }).start(); - if (appletWrap != null) { - appletWrap.stop(); - appletWrap.destroy(); + if (launcher != null) { + launcher.stop(); + launcher.destroy(); } // old minecraft versions can hang without this >_< diff --git a/libraries/launcher/org/prismlauncher/launcher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java index a41e4af6..7bf9ba0d 100644 --- a/libraries/launcher/org/prismlauncher/launcher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -53,6 +53,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.prismlauncher.launcher; public interface Launcher { diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java index fb1540bf..ed12cdf6 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java @@ -70,7 +70,9 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -@SuppressWarnings("removal") +/** + * Used to launch old versions that support applets. + */ public final class LegacyLauncher extends AbstractLauncher { private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); From ade7526f5fb1d9b8cc5f8b90904192474ba145b6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 29 Oct 2022 15:22:24 +0100 Subject: [PATCH 013/277] Remove the need for LegacyUtils Signed-off-by: TheKodeToad --- .../launcher/impl/LegacyLauncher.java | 27 +++++- .../org/prismlauncher/utils/LegacyUtils.java | 90 ------------------- 2 files changed, 25 insertions(+), 92 deletions(-) delete mode 100644 libraries/launcher/org/prismlauncher/utils/LegacyUtils.java diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java index ed12cdf6..c249c714 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java @@ -56,7 +56,6 @@ package org.prismlauncher.launcher.impl; import org.prismlauncher.applet.LegacyFrame; -import org.prismlauncher.utils.LegacyUtils; import org.prismlauncher.utils.Parameters; import java.applet.Applet; @@ -65,6 +64,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Collections; import java.util.List; import java.util.logging.Level; @@ -101,7 +101,7 @@ public final class LegacyLauncher extends AbstractLauncher { @Override public void launch() throws Throwable { Class main = loadMain(); - Field gameDirField = LegacyUtils.getMinecraftGameDirField(main); + Field gameDirField = getMinecraftGameDirField(main); if (gameDirField == null) { LOGGER.warning("Could not find Mineraft path field."); @@ -143,4 +143,27 @@ public final class LegacyLauncher extends AbstractLauncher { invokeMain(main); } + /** + * Finds a field that looks like a Minecraft base folder in a supplied class + * @param clazz the class to scan + * @return The found field. + */ + private static Field getMinecraftGameDirField(Class clazz) { + // Field we're looking for is always + // private static File obfuscatedName = null; + for (Field field : clazz.getDeclaredFields()) { + // Has to be File + if (field.getType() != File.class) + continue; + + // And Private Static. + if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers())) + continue; + + return field; + } + + return null; + } + } diff --git a/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java b/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java deleted file mode 100644 index 8e7cbb74..00000000 --- a/libraries/launcher/org/prismlauncher/utils/LegacyUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 TheKodeToad - * Copyright (C) 2022 solonovamax - * - * 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. - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole - * combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish to do - * so, delete this exception statement from your version. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.prismlauncher.utils; - -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -public final class LegacyUtils { - - private LegacyUtils() { - } - - /** - * Finds a field that looks like a Minecraft base folder in a supplied class - * - * @param clazz the class to scan - */ - public static Field getMinecraftGameDirField(Class clazz) { - // Field we're looking for is always - // private static File obfuscatedName = null; - for (Field field : clazz.getDeclaredFields()) { - // Has to be File - if (field.getType() != File.class) - continue; - - // And Private Static. - if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers())) - continue; - - return field; - } - - return null; - } - -} - From 0cd1d7aa7e6580eaed5e32c53538678bbcdd6abc Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 29 Oct 2022 15:24:55 +0100 Subject: [PATCH 014/277] Excessive safety Signed-off-by: TheKodeToad --- libraries/launcher/net/minecraft/Launcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index aef09ca9..147c727e 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -93,7 +93,7 @@ public final class Launcher extends Applet implements AppletStub { try { if (documentBase != null) { this.documentBase = documentBase; - } else if (applet.getClass().getPackage().getName().startsWith("com.mojang")) { + } else if (applet.getClass().getPackage().getName().startsWith("com.mojang.")) { // Special case only for Classic versions // TODO: 2022-10-27 Can this be changed to https From 35d200356fdeb2d54c0ad0537c9c3b4cd9a8eb4d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 29 Oct 2022 15:31:41 +0100 Subject: [PATCH 015/277] Create launcher.impl.legacy Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 5 ++--- .../launcher/org/prismlauncher/launcher/LauncherFactory.java | 2 +- .../{applet => launcher/impl/legacy}/LegacyFrame.java | 2 +- .../launcher/impl/{ => legacy}/LegacyLauncher.java | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) rename libraries/launcher/org/prismlauncher/{applet => launcher/impl/legacy}/LegacyFrame.java (99%) rename libraries/launcher/org/prismlauncher/launcher/impl/{ => legacy}/LegacyLauncher.java (98%) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index d74c3641..f85c2354 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -11,14 +11,13 @@ set(SRC org/prismlauncher/launcher/Launcher.java org/prismlauncher/launcher/LauncherFactory.java org/prismlauncher/launcher/LauncherProvider.java + org/prismlauncher/launcher/impl/legacy/LegacyFrame.java + org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java org/prismlauncher/launcher/impl/AbstractLauncher.java - org/prismlauncher/launcher/impl/LegacyLauncher.java org/prismlauncher/launcher/impl/StandardLauncher.java - org/prismlauncher/applet/LegacyFrame.java org/prismlauncher/exception/ParameterNotFoundException.java org/prismlauncher/exception/ParseException.java org/prismlauncher/utils/Parameters.java - org/prismlauncher/utils/LegacyUtils.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index e6de35fa..22e92632 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -56,8 +56,8 @@ package org.prismlauncher.launcher; -import org.prismlauncher.launcher.impl.LegacyLauncher; import org.prismlauncher.launcher.impl.StandardLauncher; +import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; import org.prismlauncher.utils.Parameters; import java.util.HashMap; diff --git a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java similarity index 99% rename from libraries/launcher/org/prismlauncher/applet/LegacyFrame.java rename to libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 58125160..12196fd4 100644 --- a/libraries/launcher/org/prismlauncher/applet/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -54,7 +54,7 @@ * limitations under the License. */ -package org.prismlauncher.applet; +package org.prismlauncher.launcher.impl.legacy; import net.minecraft.Launcher; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java similarity index 98% rename from libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java rename to libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index c249c714..d7a98352 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -53,9 +53,9 @@ * limitations under the License. */ -package org.prismlauncher.launcher.impl; +package org.prismlauncher.launcher.impl.legacy; -import org.prismlauncher.applet.LegacyFrame; +import org.prismlauncher.launcher.impl.AbstractLauncher; import org.prismlauncher.utils.Parameters; import java.applet.Applet; From 609b30110ba93c2cb3863bd3c857be7af23275cc Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 30 Oct 2022 15:27:24 +0000 Subject: [PATCH 016/277] Make main static Signed-off-by: TheKodeToad Inline local variable Signed-off-by: TheKodeToad --- .../launcher/org/prismlauncher/EntryPoint.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 40494b7c..aab7d110 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -70,12 +70,8 @@ public final class EntryPoint { private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - private final Parameters params = new Parameters(); - public static void main(String[] args) { - EntryPoint listener = new EntryPoint(); - - ExitCode exitCode = listener.listen(); + ExitCode exitCode = listen(); if (exitCode != ExitCode.NORMAL) { LOGGER.warning("Exiting with " + exitCode); @@ -112,7 +108,8 @@ public final class EntryPoint { } } - public ExitCode listen() { + private static ExitCode listen() { + Parameters parameters = new Parameters(); PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; try (BufferedReader reader = new BufferedReader(new InputStreamReader( @@ -123,7 +120,7 @@ public final class EntryPoint { while (preLaunchAction == PreLaunchAction.PROCEED) { if ((line = reader.readLine()) != null) { - preLaunchAction = parseLine(line, this.params); + preLaunchAction = parseLine(line, parameters); } else { preLaunchAction = PreLaunchAction.ABORT; } @@ -142,7 +139,7 @@ public final class EntryPoint { } try { - Launcher launcher = LauncherFactory.createLauncher(params); + Launcher launcher = LauncherFactory.createLauncher(parameters); launcher.launch(); From ac5b74301e7be057bf08aa78cf0cfbece27cd1c1 Mon Sep 17 00:00:00 2001 From: solonovamax Date: Mon, 31 Oct 2022 15:56:53 -0400 Subject: [PATCH 017/277] Cleanup splitting string into a pair Signed-off-by: solonovamax --- .../org/prismlauncher/EntryPoint.java | 90 +++++++++---------- .../launcher/impl/AbstractLauncher.java | 13 +-- .../org/prismlauncher/utils/StringUtils.java | 15 ++++ 3 files changed, 63 insertions(+), 55 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/utils/StringUtils.java diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index aab7d110..987dcc12 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -54,10 +54,12 @@ package org.prismlauncher; + import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherFactory; import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.StringUtils; import java.io.BufferedReader; import java.io.IOException; @@ -66,58 +68,47 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Level; import java.util.logging.Logger; + public final class EntryPoint { - private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - + public static void main(String[] args) { ExitCode exitCode = listen(); - + if (exitCode != ExitCode.NORMAL) { LOGGER.warning("Exiting with " + exitCode); - + System.exit(exitCode.numericalCode); } } - - private static PreLaunchAction parseLine(String inData, Parameters params) throws ParseException { - if (inData.isEmpty()) + + private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { + if (input.isEmpty()) throw new ParseException("Unexpected empty string!"); - - String first = inData; - String second = null; - int splitPoint = inData.indexOf(' '); - - if (splitPoint != -1) { - first = first.substring(0, splitPoint); - second = inData.substring(splitPoint + 1); - } - - switch (first) { - case "launch": - return PreLaunchAction.LAUNCH; - case "abort": - return PreLaunchAction.ABORT; - default: - if (second == null || second.isEmpty()) - throw new ParseException("Error while parsing:" + inData); - - params.add(first, second); - - return PreLaunchAction.PROCEED; + + + if ("launch".equalsIgnoreCase(input)) { + return PreLaunchAction.LAUNCH; + } else if ("abort".equalsIgnoreCase(input)) { + return PreLaunchAction.ABORT; + } else { + String[] pair = StringUtils.splitStringPair(' ', input); + if (pair == null) + throw new ParseException("Error while parsing:" + input); + + params.add(pair[0], pair[1]); + + return PreLaunchAction.PROCEED; } } - + private static ExitCode listen() { Parameters parameters = new Parameters(); PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; - - try (BufferedReader reader = new BufferedReader(new InputStreamReader( - System.in, - StandardCharsets.UTF_8 - ))) { + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8))) { String line; - + while (preLaunchAction == PreLaunchAction.PROCEED) { if ((line = reader.readLine()) != null) { preLaunchAction = parseLine(line, parameters); @@ -127,49 +118,50 @@ public final class EntryPoint { } } catch (IOException | ParseException e) { LOGGER.log(Level.SEVERE, "Launcher abort due to exception:", e); - + return ExitCode.ERROR; } - + // Main loop if (preLaunchAction == PreLaunchAction.ABORT) { LOGGER.info("Launch aborted by the launcher."); - + return ExitCode.ERROR; } - + try { Launcher launcher = LauncherFactory.createLauncher(parameters); - + launcher.launch(); - + return ExitCode.NORMAL; } catch (IllegalArgumentException e) { LOGGER.log(Level.SEVERE, "Wrong argument.", e); - + return ExitCode.ERROR; } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); - + return ExitCode.ERROR; } } - + private enum PreLaunchAction { PROCEED, LAUNCH, ABORT } - + + private enum ExitCode { NORMAL(0), ERROR(1); - + private final int numericalCode; - + ExitCode(int numericalCode) { this.numericalCode = numericalCode; } } - + } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 7a622b1b..c572db10 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -58,6 +58,7 @@ package org.prismlauncher.launcher.impl; import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.StringUtils; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -101,13 +102,13 @@ public abstract class AbstractLauncher implements Launcher { height = DEFAULT_WINDOW_HEIGHT; } else { maximize = false; - - int byIndex = windowParams.indexOf('x'); - - if (byIndex != -1) { + + String[] sizePair = StringUtils.splitStringPair('x', windowParams); + + if (sizePair != null) { try { - width = Integer.parseInt(windowParams.substring(0, byIndex)); - height = Integer.parseInt(windowParams.substring(byIndex + 1)); + width = Integer.parseInt(sizePair[0]); + height = Integer.parseInt(sizePair[1]); return; } catch (NumberFormatException ignored) { } diff --git a/libraries/launcher/org/prismlauncher/utils/StringUtils.java b/libraries/launcher/org/prismlauncher/utils/StringUtils.java new file mode 100644 index 00000000..9058e4b3 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/StringUtils.java @@ -0,0 +1,15 @@ +package org.prismlauncher.utils; + + +public final class StringUtils { + private StringUtils() { + } + + public static String[] splitStringPair(char splitChar, String input) { + int splitPoint = input.indexOf(splitChar); + if (splitPoint == -1) + return null; + + return new String[]{ input.substring(0, splitPoint), input.substring(splitPoint + 1) }; + } +} From 9b8096c6993df68ac99c5c24483e169fbec60979 Mon Sep 17 00:00:00 2001 From: solonovamax Date: Tue, 1 Nov 2022 11:27:31 -0400 Subject: [PATCH 018/277] Get rid of gross HashMap being used by `LauncherFactory` and instead use *fancy* enum Signed-off-by: solonovamax --- .../launcher/LauncherFactory.java | 49 +++++++-------- .../launcher/impl/StandardLauncher.java | 23 +++++-- .../launcher/impl/legacy/LegacyLauncher.java | 62 ++++++++++++------- 3 files changed, 84 insertions(+), 50 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index 22e92632..6f9be63c 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -56,43 +56,42 @@ package org.prismlauncher.launcher; + import org.prismlauncher.launcher.impl.StandardLauncher; import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; import org.prismlauncher.utils.Parameters; -import java.util.HashMap; -import java.util.Map; public final class LauncherFactory { - - private static final Map launcherRegistry = new HashMap<>(); - - static { - launcherRegistry.put("standard", new LauncherProvider() { - @Override - public Launcher provide(Parameters parameters) { - return new StandardLauncher(parameters); - } - }); - launcherRegistry.put("legacy", new LauncherProvider() { - @Override - public Launcher provide(Parameters parameters) { - return new LegacyLauncher(parameters); - } - }); - } private LauncherFactory() { } - public static Launcher createLauncher(Parameters parameters) { - String name = parameters.getString("launcher"); + public static Launcher createLauncher(String launcherType, Parameters parameters) { + return createLauncher(LauncherType.valueOf(launcherType.toUpperCase()), parameters); + } - LauncherProvider launcherProvider = launcherRegistry.get(name); - - if (launcherProvider == null) - throw new IllegalArgumentException("Invalid launcher type: " + name); + public static Launcher createLauncher(LauncherType launcherType, Parameters parameters) { + LauncherProvider launcherProvider = launcherType.getLauncherProvider(); return launcherProvider.provide(parameters); } + public static Launcher createLauncher(Parameters parameters) { + return createLauncher(parameters.getString("launcher"), parameters); + } + + public enum LauncherType { + STANDARD(StandardLauncher.getProvider()), + LEGACY(LegacyLauncher.getProvider()); + + private final LauncherProvider launcherProvider; + + LauncherType(LauncherProvider launcherProvider) { + this.launcherProvider = launcherProvider; + } + + public LauncherProvider getLauncherProvider() { + return launcherProvider; + } + } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 5c884e99..24b12c95 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -54,15 +54,23 @@ package org.prismlauncher.launcher.impl; + +import org.prismlauncher.launcher.Launcher; +import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.utils.Parameters; + public final class StandardLauncher extends AbstractLauncher { - public StandardLauncher(Parameters params) { - super(params); - } + public StandardLauncher(Parameters params) { + super(params); + } - @Override + public static LauncherProvider getProvider() { + return new StandardLauncherProvider(); + } + + @Override public void launch() throws Throwable { // window size, title and state @@ -87,4 +95,11 @@ public final class StandardLauncher extends AbstractLauncher { loadAndInvokeMain(); } + + private static class StandardLauncherProvider implements LauncherProvider { + @Override + public Launcher provide(Parameters parameters) { + return new StandardLauncher(parameters); + } + } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index d7a98352..e342e788 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -55,6 +55,9 @@ package org.prismlauncher.launcher.impl.legacy; + +import org.prismlauncher.launcher.Launcher; +import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.launcher.impl.AbstractLauncher; import org.prismlauncher.utils.Parameters; @@ -70,6 +73,7 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; + /** * Used to launch old versions that support applets. */ @@ -78,10 +82,13 @@ public final class LegacyLauncher extends AbstractLauncher { private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); private final String user, session; + private final String title; + private final String appletClass; private final boolean noApplet; + private final String cwd; public LegacyLauncher(Parameters params) { @@ -98,6 +105,35 @@ public final class LegacyLauncher extends AbstractLauncher { cwd = System.getProperty("user.dir"); } + public static LauncherProvider getProvider() { + return new LegacyLauncherProvider(); + } + + /** + * Finds a field that looks like a Minecraft base folder in a supplied class + * + * @param clazz the class to scan + * + * @return The found field. + */ + private static Field getMinecraftGameDirField(Class clazz) { + // Field we're looking for is always + // private static File obfuscatedName = null; + for (Field field : clazz.getDeclaredFields()) { + // Has to be File + if (field.getType() != File.class) + continue; + + // And Private Static. + if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers())) + continue; + + return field; + } + + return null; + } + @Override public void launch() throws Throwable { Class main = loadMain(); @@ -130,7 +166,7 @@ public final class LegacyLauncher extends AbstractLauncher { serverAddress, serverPort, mcParams.contains("--demo") - ); + ); return; } catch (Throwable e) { @@ -143,27 +179,11 @@ public final class LegacyLauncher extends AbstractLauncher { invokeMain(main); } - /** - * Finds a field that looks like a Minecraft base folder in a supplied class - * @param clazz the class to scan - * @return The found field. - */ - private static Field getMinecraftGameDirField(Class clazz) { - // Field we're looking for is always - // private static File obfuscatedName = null; - for (Field field : clazz.getDeclaredFields()) { - // Has to be File - if (field.getType() != File.class) - continue; - // And Private Static. - if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers())) - continue; - - return field; + private static class LegacyLauncherProvider implements LauncherProvider { + @Override + public Launcher provide(Parameters parameters) { + return new LegacyLauncher(parameters); } - - return null; } - } From dabb84f62a35ea67793425f9118ea6a5bca96e00 Mon Sep 17 00:00:00 2001 From: solonovamax Date: Tue, 1 Nov 2022 12:27:04 -0400 Subject: [PATCH 019/277] Cleanup launcher classes Cleanup a bunch of the code in launcher classes - Migrate the majority of the reflection to ReflectionUtils - Decrease logic in AbstractLauncher - Add logging to launcher classes at FINE level - make mcParams in AbstractLauncher an immutable list to prevent runtime manipulation - StandardLauncher instead copies the list to modify it Signed-off-by: solonovamax --- libraries/launcher/CMakeLists.txt | 1 + .../exception/ParameterNotFoundException.java | 22 ++- .../exception/ParseException.java | 21 +++ .../launcher/impl/AbstractLauncher.java | 67 ++++----- .../launcher/impl/StandardLauncher.java | 36 +++-- .../launcher/impl/legacy/LegacyLauncher.java | 80 +++-------- .../org/prismlauncher/utils/Parameters.java | 4 +- .../prismlauncher/utils/ReflectionUtils.java | 131 ++++++++++++++++++ 8 files changed, 248 insertions(+), 114 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index f85c2354..45a43b93 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC org/prismlauncher/exception/ParameterNotFoundException.java org/prismlauncher/exception/ParseException.java org/prismlauncher/utils/Parameters.java + org/prismlauncher/utils/ReflectionUtils.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) diff --git a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java index c083e02a..3dd6efc3 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java @@ -54,10 +54,28 @@ package org.prismlauncher.exception; + +@SuppressWarnings("serial") public final class ParameterNotFoundException extends IllegalArgumentException { - public ParameterNotFoundException(String key) { - super("Unknown parameter name: " + key); + public ParameterNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public ParameterNotFoundException(Throwable cause) { + super(cause); + } + + public ParameterNotFoundException(String message) { + super(message); + } + + public ParameterNotFoundException() { + super(); + } + + public static ParameterNotFoundException forParameterName(String parameterName) { + return new ParameterNotFoundException(String.format("Unknown parameter name '%s'", parameterName)); } } diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 8904f9ee..2243f23f 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -54,10 +54,31 @@ package org.prismlauncher.exception; + +@SuppressWarnings({ "serial", "unused" }) public final class ParseException extends IllegalArgumentException { public ParseException(String message) { super(message); } + public ParseException(String message, Throwable cause) { + super(message, cause); + } + + public ParseException(Throwable cause) { + super(cause); + } + + public ParseException() { + super(); + } + + public static ParseException forInputString(String inputString) { + return new ParseException(String.format("Could not parse input string '%s'", inputString)); + } + + public static ParseException forInputString(String inputString, Throwable cause) { + return new ParseException(String.format("Could not parse input string '%s'", inputString), cause); + } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index c572db10..9dd7df10 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -55,81 +55,66 @@ package org.prismlauncher.launcher.impl; + import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.StringUtils; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.util.ArrayList; +import java.util.Collections; import java.util.List; + public abstract class AbstractLauncher implements Launcher { private static final int DEFAULT_WINDOW_WIDTH = 854; + private static final int DEFAULT_WINDOW_HEIGHT = 480; // parameters, separated from ParamBucket protected final List mcParams; - private final String mainClass; // secondary parameters protected final int width; + protected final int height; + protected final boolean maximize; - protected final String serverAddress, serverPort; + protected final String serverAddress; - protected final ClassLoader classLoader; + protected final String serverPort; + + protected final String mainClassName; protected AbstractLauncher(Parameters params) { - classLoader = ClassLoader.getSystemClassLoader(); + this.mcParams = Collections.unmodifiableList(params.getList("param", new ArrayList())); + this.mainClassName = params.getString("mainClass", "net.minecraft.client.Minecraft"); - mcParams = params.getList("param", new ArrayList()); - mainClass = params.getString("mainClass", "net.minecraft.client.Minecraft"); - - serverAddress = params.getString("serverAddress", null); - serverPort = params.getString("serverPort", null); + this.serverAddress = params.getString("serverAddress", null); + this.serverPort = params.getString("serverPort", null); String windowParams = params.getString("windowParams", null); - if ("max".equals(windowParams) || windowParams == null) { - maximize = windowParams != null; + this.maximize = "max".equalsIgnoreCase(windowParams); - width = DEFAULT_WINDOW_WIDTH; - height = DEFAULT_WINDOW_HEIGHT; - } else { - maximize = false; - + if (windowParams != null && !"max".equalsIgnoreCase(windowParams)) { String[] sizePair = StringUtils.splitStringPair('x', windowParams); - + if (sizePair != null) { try { - width = Integer.parseInt(sizePair[0]); - height = Integer.parseInt(sizePair[1]); - return; - } catch (NumberFormatException ignored) { + this.width = Integer.parseInt(sizePair[0]); + this.height = Integer.parseInt(sizePair[1]); + } catch (NumberFormatException e) { + throw new ParseException(String.format("Could not parse window parameters from '%s'", windowParams), e); } + } else { + throw new ParseException(String.format("Invalid window size parameters '%s'. Format: [height]x[width]", windowParams)); } - - throw new ParseException("Invalid window size parameter value: " + windowParams); + } else { + this.width = DEFAULT_WINDOW_WIDTH; + this.height = DEFAULT_WINDOW_HEIGHT; } } - - protected Class loadMain() throws ClassNotFoundException { - return classLoader.loadClass(mainClass); - } - - protected void loadAndInvokeMain() throws Throwable { - invokeMain(loadMain()); - } - - protected void invokeMain(Class mainClass) throws Throwable { - MethodHandle method = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); - - method.invokeExact(mcParams.toArray(new String[0])); - } - } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 24b12c95..fc0c9823 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -58,9 +58,17 @@ package org.prismlauncher.launcher.impl; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.ReflectionUtils; + +import java.lang.invoke.MethodHandle; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; public final class StandardLauncher extends AbstractLauncher { + private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); + public StandardLauncher(Parameters params) { super(params); @@ -78,21 +86,27 @@ public final class StandardLauncher extends AbstractLauncher { // the following often breaks linux screen setups // mcparams.add("--fullscreen"); - if (!maximize) { - mcParams.add("--width"); - mcParams.add(Integer.toString(width)); - mcParams.add("--height"); - mcParams.add(Integer.toString(height)); + List launchParameters = new ArrayList<>(this.mcParams); + + if (!this.maximize) { + launchParameters.add("--width"); + launchParameters.add(Integer.toString(width)); + launchParameters.add("--height"); + launchParameters.add(Integer.toString(height)); } - if (serverAddress != null) { - mcParams.add("--server"); - mcParams.add(serverAddress); - mcParams.add("--port"); - mcParams.add(serverPort); + if (this.serverAddress != null) { + launchParameters.add("--server"); + launchParameters.add(serverAddress); + launchParameters.add("--port"); + launchParameters.add(serverPort); } - loadAndInvokeMain(); + LOGGER.info("Launching minecraft using the main class entrypoint"); + + MethodHandle method = ReflectionUtils.findMainEntrypoint(this.mainClassName); + + method.invokeExact((Object[]) launchParameters.toArray(new String[0])); } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index e342e788..0ce3c57b 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -60,14 +60,11 @@ import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.launcher.impl.AbstractLauncher; import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.ReflectionUtils; -import java.applet.Applet; import java.io.File; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.Collections; import java.util.List; import java.util.logging.Level; @@ -87,7 +84,7 @@ public final class LegacyLauncher extends AbstractLauncher { private final String appletClass; - private final boolean noApplet; + private final boolean usesApplet; private final String cwd; @@ -100,8 +97,9 @@ public final class LegacyLauncher extends AbstractLauncher { appletClass = params.getString("appletClass", "net.minecraft.client.MinecraftApplet"); List traits = params.getList("traits", Collections.emptyList()); - noApplet = traits.contains("noapplet"); + usesApplet = !traits.contains("noapplet"); + //noinspection AccessOfSystemProperties cwd = System.getProperty("user.dir"); } @@ -109,74 +107,40 @@ public final class LegacyLauncher extends AbstractLauncher { return new LegacyLauncherProvider(); } - /** - * Finds a field that looks like a Minecraft base folder in a supplied class - * - * @param clazz the class to scan - * - * @return The found field. - */ - private static Field getMinecraftGameDirField(Class clazz) { - // Field we're looking for is always - // private static File obfuscatedName = null; - for (Field field : clazz.getDeclaredFields()) { - // Has to be File - if (field.getType() != File.class) - continue; - - // And Private Static. - if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers())) - continue; - - return field; - } - - return null; - } - @Override public void launch() throws Throwable { - Class main = loadMain(); - Field gameDirField = getMinecraftGameDirField(main); + Class main = ClassLoader.getSystemClassLoader().loadClass(this.mainClassName); + Field gameDirField = ReflectionUtils.getMinecraftGameDirField(main); if (gameDirField == null) { - LOGGER.warning("Could not find Mineraft path field."); + LOGGER.warning("Could not find Minecraft path field"); } else { gameDirField.setAccessible(true); - gameDirField.set(null, new File(cwd)); + gameDirField.set(null /* field is static, so instance is null */, new File(cwd)); } - if (!noApplet) { - LOGGER.info("Launching with applet wrapper..."); + if (this.usesApplet) { + LOGGER.info("Launching legacy minecraft using applet wrapper..."); try { - Class appletClass = classLoader.loadClass(this.appletClass); - - MethodHandle constructor = MethodHandles.lookup().findConstructor(appletClass, MethodType.methodType(void.class)); - Applet applet = (Applet) constructor.invoke(); - - LegacyFrame window = new LegacyFrame(title, applet); + LegacyFrame window = new LegacyFrame(title, ReflectionUtils.createAppletClass(this.appletClass)); window.start( - user, - session, - width, - height, - maximize, - serverAddress, - serverPort, - mcParams.contains("--demo") + this.user, + this.session, + this.width, this.height, this.maximize, + this.serverAddress, this.serverPort, + this.mcParams.contains("--demo") ); - - return; } catch (Throwable e) { - LOGGER.log(Level.SEVERE, "Applet wrapper failed:", e); - - LOGGER.warning("Falling back to using main class."); + LOGGER.log(Level.SEVERE, "Running applet wrapper failed with exception", e); } - } + } else { + LOGGER.info("Launching legacy minecraft using the main class entrypoint"); + MethodHandle method = ReflectionUtils.findMainEntrypoint(main); - invokeMain(main); + method.invokeExact((Object[]) mcParams.toArray(new String[0])); + } } diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 5596e88a..6fbd0ef1 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -83,7 +83,7 @@ public final class Parameters { List params = map.get(key); if (params == null) - throw new ParameterNotFoundException(key); + throw ParameterNotFoundException.forParameterName(key); return params; } @@ -101,7 +101,7 @@ public final class Parameters { List list = getList(key); if (list.isEmpty()) - throw new ParameterNotFoundException(key); + throw ParameterNotFoundException.forParameterName(key); return list.get(0); } diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java new file mode 100644 index 00000000..484e0d8a --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -0,0 +1,131 @@ +package org.prismlauncher.utils; + + +import java.applet.Applet; +import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.logging.Level; +import java.util.logging.Logger; + + +public final class ReflectionUtils { + private static final Logger LOGGER = Logger.getLogger("ReflectionUtils"); + + private ReflectionUtils() { + } + + /** + * Instantiate an applet class by name + * + * @param appletClassName The name of the applet class to resolve + * + * @return The instantiated applet class + * + * @throws ClassNotFoundException if the provided class name cannot be found + * @throws NoSuchMethodException if the no-args constructor cannot be found + * @throws IllegalAccessException if the constructor cannot be accessed via method handles + * @throws Throwable any exceptions from the class's constructor + */ + public static Applet createAppletClass(String appletClassName) throws Throwable { + Class appletClass = ClassLoader.getSystemClassLoader().loadClass(appletClassName); + + MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, MethodType.methodType(void.class)); + return (Applet) appletConstructor.invoke(); + } + + /** + * Finds a field that looks like a Minecraft base folder in a supplied class + * + * @param minecraftMainClass the class to scan + * + * @return The found field. + */ + public static Field getMinecraftGameDirField(Class minecraftMainClass) { + LOGGER.fine("Resolving minecraft game directory field"); + // Field we're looking for is always + // private static File obfuscatedName = null; + for (Field field : minecraftMainClass.getDeclaredFields()) { + // Has to be File + if (field.getType() != File.class) { + continue; + } + + int fieldModifiers = field.getModifiers(); + + + // Must be static + if (!Modifier.isStatic(fieldModifiers)) { + LOGGER.log(Level.FINE, "Rejecting field {0} because it is not static", field.getName()); + continue; + } + + // Must be private + if (!Modifier.isPrivate(fieldModifiers)) { + LOGGER.log(Level.FINE, "Rejecting field {0} because it is not private", field.getName()); + continue; + } + + // Must not be final + if (Modifier.isFinal(fieldModifiers)) { + LOGGER.log(Level.FINE, "Rejecting field {0} because it is final", field.getName()); + continue; + } + + LOGGER.log(Level.FINE, "Identified field {0} to match conditions for minecraft game directory field", field.getName()); + + return field; + } + + return null; + } + + /** + * Resolve main entrypoint and returns method handle for it. + *

+ * Resolves a method that matches the following signature + * + * public static void main(String[] args) { + *

+ * } + * + * + * @param entrypointClass The entrypoint class to resolve the method from + * + * @return The method handle for the resolved entrypoint + * + * @throws NoSuchMethodException If no method matching the correct signature can be found + * @throws IllegalAccessException If method handles cannot access the entrypoint + */ + public static MethodHandle findMainEntrypoint(Class entrypointClass) throws NoSuchMethodException, IllegalAccessException { + return MethodHandles.lookup().findStatic(entrypointClass, "main", MethodType.methodType(void.class, String[].class)); + } + + /** + * Resolve main entrypoint and returns method handle for it. + *

+ * Resolves a method that matches the following signature + * + * public static void main(String[] args) { + *

+ * } + * + * + * @param entrypointClassName The name of the entrypoint class to resolve the method from + * + * @return The method handle for the resolved entrypoint + * + * @throws ClassNotFoundException If a class cannot be found with the provided name + * @throws NoSuchMethodException If no method matching the correct signature can be found + * @throws IllegalAccessException If method handles cannot access the entrypoint + */ + public static MethodHandle findMainEntrypoint(String entrypointClassName) throws + ClassNotFoundException, + NoSuchMethodException, + IllegalAccessException { + return findMainEntrypoint(ClassLoader.getSystemClassLoader().loadClass(entrypointClassName)); + } +} From e899e31745e2f10fdd404127750b6ee22364d405 Mon Sep 17 00:00:00 2001 From: solonovamax Date: Tue, 1 Nov 2022 12:34:13 -0400 Subject: [PATCH 020/277] More informative exceptions in entrypoint Signed-off-by: solonovamax --- .../org/prismlauncher/EntryPoint.java | 83 +++++++++++-------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 987dcc12..07af4e55 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -71,22 +71,26 @@ import java.util.logging.Logger; public final class EntryPoint { private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - + + private EntryPoint() { + } + public static void main(String[] args) { ExitCode exitCode = listen(); - + if (exitCode != ExitCode.NORMAL) { LOGGER.warning("Exiting with " + exitCode); - + + //noinspection CallToSystemExit System.exit(exitCode.numericalCode); } } - + private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { - if (input.isEmpty()) - throw new ParseException("Unexpected empty string!"); - - + if (input.isEmpty()) // TODO: 2022-11-01 Should we just ignore this? + throw new ParseException("Unexpected empty string! You should not pass empty newlines to stdin."); + + if ("launch".equalsIgnoreCase(input)) { return PreLaunchAction.LAUNCH; } else if ("abort".equalsIgnoreCase(input)) { @@ -94,22 +98,26 @@ public final class EntryPoint { } else { String[] pair = StringUtils.splitStringPair(' ', input); if (pair == null) - throw new ParseException("Error while parsing:" + input); - + throw new ParseException(String.format( + "Could not split input string '%s' by space. All input provided from stdin must be either 'launch', 'abort', or " + + "in the format '[param name] [param]'.", + input)); + params.add(pair[0], pair[1]); - + return PreLaunchAction.PROCEED; } } - + private static ExitCode listen() { Parameters parameters = new Parameters(); PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; - + try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8))) { String line; - + while (preLaunchAction == PreLaunchAction.PROCEED) { + //noinspection NestedAssignment if ((line = reader.readLine()) != null) { preLaunchAction = parseLine(line, parameters); } else { @@ -117,51 +125,58 @@ public final class EntryPoint { } } } catch (IOException | ParseException e) { - LOGGER.log(Level.SEVERE, "Launcher abort due to exception:", e); - - return ExitCode.ERROR; + LOGGER.log(Level.SEVERE, "Launcher abort due to exception", e); + + return ExitCode.ILLEGAL_ARGUMENT; } - + // Main loop if (preLaunchAction == PreLaunchAction.ABORT) { LOGGER.info("Launch aborted by the launcher."); - - return ExitCode.ERROR; + + return ExitCode.ABORT; } - + try { Launcher launcher = LauncherFactory.createLauncher(parameters); - + launcher.launch(); - + return ExitCode.NORMAL; } catch (IllegalArgumentException e) { LOGGER.log(Level.SEVERE, "Wrong argument.", e); - - return ExitCode.ERROR; + + return ExitCode.ILLEGAL_ARGUMENT; + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + LOGGER.log(Level.SEVERE, "Caught reflection exception from launcher", e); + + return ExitCode.REFLECTION_EXCEPTION; } catch (Throwable e) { - LOGGER.log(Level.SEVERE, "Exception caught from launcher.", e); - + LOGGER.log(Level.SEVERE, "Exception caught from launcher", e); + return ExitCode.ERROR; } } - + private enum PreLaunchAction { PROCEED, LAUNCH, ABORT } - - + + private enum ExitCode { NORMAL(0), - ERROR(1); - + ABORT(1), + ERROR(2), + ILLEGAL_ARGUMENT(3), + REFLECTION_EXCEPTION(4); + private final int numericalCode; - + ExitCode(int numericalCode) { this.numericalCode = numericalCode; } } - + } From afe088dba18b83ae1ea79e3b31ef5026f68b7b6d Mon Sep 17 00:00:00 2001 From: solonovamax Date: Wed, 2 Nov 2022 15:10:05 -0400 Subject: [PATCH 021/277] Fix license headers - Update license headers in several files to remove multimc apache reference, when unneeded - LauncherFactory: we've entirely rewritten this class at this point, so it's fully under GPL code - Launcher: this interface contains zero logic, and only a single method named `launch`, so I doubt you can actually copyright that - LauncherProvider: same as above - ParseException, ParameterNotFoundException: same as above; this class contains almost no logic (And the logic that was added is under GPL) - ReflectionUtils, StringUtils: add license header - Everything else: modify the program name in the license header from "PolyMC - Minecraft Launcher" to "Prism Launcher" Signed-off-by: solonovamax --- .../launcher/net/minecraft/Launcher.java | 3 +- .../org/prismlauncher/EntryPoint.java | 3 +- .../exception/ParameterNotFoundException.java | 20 +--------- .../exception/ParseException.java | 20 +--------- .../org/prismlauncher/launcher/Launcher.java | 20 +--------- .../launcher/LauncherFactory.java | 20 +--------- .../launcher/LauncherProvider.java | 20 +--------- .../launcher/impl/AbstractLauncher.java | 3 +- .../launcher/impl/StandardLauncher.java | 3 +- .../launcher/impl/legacy/LegacyFrame.java | 7 +++- .../launcher/impl/legacy/LegacyLauncher.java | 3 +- .../org/prismlauncher/utils/Parameters.java | 3 +- .../prismlauncher/utils/ReflectionUtils.java | 38 ++++++++++++++++++ .../org/prismlauncher/utils/StringUtils.java | 40 ++++++++++++++++++- 14 files changed, 103 insertions(+), 100 deletions(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 147c727e..8dff5692 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 07af4e55..444e665f 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax diff --git a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java index 3dd6efc3..48bf9f4c 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Samisafool @@ -33,23 +34,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ package org.prismlauncher.exception; diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 2243f23f..0d5f3c10 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Samisafool @@ -33,23 +34,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ package org.prismlauncher.exception; diff --git a/libraries/launcher/org/prismlauncher/launcher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java index 7bf9ba0d..d54cfd5c 100644 --- a/libraries/launcher/org/prismlauncher/launcher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Samisafool @@ -35,23 +36,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ package org.prismlauncher.launcher; diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index 6f9be63c..03a76d29 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Samisafool @@ -35,23 +36,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ package org.prismlauncher.launcher; diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java index 66d23c0f..73d0cdc5 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 solonovamax * @@ -32,23 +33,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ package org.prismlauncher.launcher; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 9dd7df10..ac7a6223 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index fc0c9823..61709bf7 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 12196fd4..391aa702 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 flow @@ -56,11 +57,13 @@ package org.prismlauncher.launcher.impl.legacy; + import net.minecraft.Launcher; import javax.imageio.ImageIO; import java.applet.Applet; -import java.awt.*; +import java.awt.Dimension; +import java.awt.Frame; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 0ce3c57b..17cdeb08 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 flow * Copyright (C) 2022 TheKodeToad diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 6fbd0ef1..d019aa41 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher + * * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Samisafool diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index 484e0d8a..b0eefec7 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -1,3 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher + * + * Copyright (C) 2022 solonovamax + * Copyright (C) 2022 TheKodeToad + * Copyright (C) 2022 icelimetea + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package org.prismlauncher.utils; diff --git a/libraries/launcher/org/prismlauncher/utils/StringUtils.java b/libraries/launcher/org/prismlauncher/utils/StringUtils.java index 9058e4b3..d470624c 100644 --- a/libraries/launcher/org/prismlauncher/utils/StringUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/StringUtils.java @@ -1,15 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher + * + * Copyright (C) 2022 solonovamax + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package org.prismlauncher.utils; public final class StringUtils { private StringUtils() { } - + public static String[] splitStringPair(char splitChar, String input) { int splitPoint = input.indexOf(splitChar); if (splitPoint == -1) return null; - + return new String[]{ input.substring(0, splitPoint), input.substring(splitPoint + 1) }; } } From 8ce78dcc54a8ed3e6292a8650692fa9c520a993d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 16:40:23 +0000 Subject: [PATCH 022/277] Try to improve consistency - Makes code formatting more consistent with the C++ codebase. Probably removes some trailing whitespace. Maybe it would be best to commit an Eclipse or IntelliJ code format preferences file? - Removes obscure suppressions. I personally think it's better to only suppress warnings that javac complains about. Suppressing a lot of non-standardised warnings (many of them turned off by default even in IntelliJ) just creates needless clutter. - Fixes some trivial warnings instead of suppressing them. serialVersionUID is sort of stupid, but I'd rather mentally ignore it or just fix it if it's really that annoying. Signed-off-by: TheKodeToad --- libraries/launcher/net/minecraft/Launcher.java | 12 ++++++------ .../launcher/org/prismlauncher/EntryPoint.java | 14 +++++--------- .../exception/ParameterNotFoundException.java | 7 ++++--- .../prismlauncher/exception/ParseException.java | 7 +++++-- .../prismlauncher/launcher/LauncherFactory.java | 4 ++-- .../launcher/impl/AbstractLauncher.java | 14 +++----------- .../launcher/impl/StandardLauncher.java | 9 +++------ .../launcher/impl/legacy/LegacyFrame.java | 5 ++--- .../launcher/impl/legacy/LegacyLauncher.java | 13 +++---------- .../org/prismlauncher/utils/ReflectionUtils.java | 9 ++++----- .../org/prismlauncher/utils/StringUtils.java | 5 +++-- 11 files changed, 40 insertions(+), 59 deletions(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 8dff5692..796e4829 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -72,12 +72,12 @@ import java.util.TreeMap; */ public final class Launcher extends Applet implements AppletStub { + private static final long serialVersionUID = 1L; + private final Map params = new TreeMap<>(); private Applet wrappedApplet; - private final URL documentBase; - private boolean active = false; public Launcher(Applet applet) { @@ -85,11 +85,11 @@ public final class Launcher extends Applet implements AppletStub { } public Launcher(Applet applet, URL documentBase) { - this.setLayout(new BorderLayout()); + setLayout(new BorderLayout()); this.add(applet, "Center"); - this.wrappedApplet = applet; + wrappedApplet = applet; try { if (documentBase != null) { @@ -109,12 +109,12 @@ public final class Launcher extends Applet implements AppletStub { } public void replace(Applet applet) { - this.wrappedApplet = applet; + wrappedApplet = applet; applet.setStub(this); applet.setSize(getWidth(), getHeight()); - this.setLayout(new BorderLayout()); + setLayout(new BorderLayout()); this.add(applet, "Center"); applet.init(); diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 444e665f..be180d6a 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -55,7 +55,6 @@ package org.prismlauncher; - import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherFactory; @@ -69,7 +68,6 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Level; import java.util.logging.Logger; - public final class EntryPoint { private static final Logger LOGGER = Logger.getLogger("EntryPoint"); @@ -92,11 +90,11 @@ public final class EntryPoint { throw new ParseException("Unexpected empty string! You should not pass empty newlines to stdin."); - if ("launch".equalsIgnoreCase(input)) { + if ("launch".equalsIgnoreCase(input)) return PreLaunchAction.LAUNCH; - } else if ("abort".equalsIgnoreCase(input)) { + else if ("abort".equalsIgnoreCase(input)) return PreLaunchAction.ABORT; - } else { + else { String[] pair = StringUtils.splitStringPair(' ', input); if (pair == null) throw new ParseException(String.format( @@ -119,11 +117,10 @@ public final class EntryPoint { while (preLaunchAction == PreLaunchAction.PROCEED) { //noinspection NestedAssignment - if ((line = reader.readLine()) != null) { + if ((line = reader.readLine()) != null) preLaunchAction = parseLine(line, parameters); - } else { + else preLaunchAction = PreLaunchAction.ABORT; - } } } catch (IOException | ParseException e) { LOGGER.log(Level.SEVERE, "Launcher abort due to exception", e); @@ -165,7 +162,6 @@ public final class EntryPoint { ABORT } - private enum ExitCode { NORMAL(0), ABORT(1), diff --git a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java index 48bf9f4c..ad973ace 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java @@ -5,6 +5,8 @@ * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Samisafool + * Copyright (C) 2022 solonovamax + * Copyright (C) 2022 TheKodeToad * * 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 @@ -38,10 +40,10 @@ package org.prismlauncher.exception; - -@SuppressWarnings("serial") public final class ParameterNotFoundException extends IllegalArgumentException { + private static final long serialVersionUID = 1L; + public ParameterNotFoundException(String message, Throwable cause) { super(message, cause); } @@ -55,7 +57,6 @@ public final class ParameterNotFoundException extends IllegalArgumentException { } public ParameterNotFoundException() { - super(); } public static ParameterNotFoundException forParameterName(String parameterName) { diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 0d5f3c10..2fd693e7 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -5,6 +5,8 @@ * Copyright (C) 2022 icelimetea * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Samisafool + * Copyright (C) 2022 solonovamax + * Copyright (C) 2022 TheKodeToad * * 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 @@ -38,10 +40,10 @@ package org.prismlauncher.exception; - -@SuppressWarnings({ "serial", "unused" }) public final class ParseException extends IllegalArgumentException { + private static final long serialVersionUID = 1L; + public ParseException(String message) { super(message); } @@ -65,4 +67,5 @@ public final class ParseException extends IllegalArgumentException { public static ParseException forInputString(String inputString, Throwable cause) { return new ParseException(String.format("Could not parse input string '%s'", inputString), cause); } + } diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index 03a76d29..4844a774 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -40,13 +40,12 @@ package org.prismlauncher.launcher; - import org.prismlauncher.launcher.impl.StandardLauncher; import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; import org.prismlauncher.utils.Parameters; - public final class LauncherFactory { + private LauncherFactory() { } @@ -78,4 +77,5 @@ public final class LauncherFactory { return launcherProvider; } } + } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index ac7a6223..8aec7c28 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -56,7 +56,6 @@ package org.prismlauncher.launcher.impl; - import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.utils.Parameters; @@ -66,26 +65,18 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; - public abstract class AbstractLauncher implements Launcher { private static final int DEFAULT_WINDOW_WIDTH = 854; - private static final int DEFAULT_WINDOW_HEIGHT = 480; // parameters, separated from ParamBucket protected final List mcParams; // secondary parameters - protected final int width; - - protected final int height; - + protected final int width, height; protected final boolean maximize; - - protected final String serverAddress; - - protected final String serverPort; + protected final String serverAddress, serverPort; protected final String mainClassName; @@ -118,4 +109,5 @@ public abstract class AbstractLauncher implements Launcher { this.height = DEFAULT_WINDOW_HEIGHT; } } + } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 61709bf7..a3ea2f4a 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -55,7 +55,6 @@ package org.prismlauncher.launcher.impl; - import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.utils.Parameters; @@ -66,10 +65,9 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; - public final class StandardLauncher extends AbstractLauncher { - private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); + private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); public StandardLauncher(Parameters params) { super(params); @@ -105,16 +103,15 @@ public final class StandardLauncher extends AbstractLauncher { LOGGER.info("Launching minecraft using the main class entrypoint"); - MethodHandle method = ReflectionUtils.findMainEntrypoint(this.mainClassName); - + MethodHandle method = ReflectionUtils.findMainMethod(this.mainClassName); method.invokeExact((Object[]) launchParameters.toArray(new String[0])); } - private static class StandardLauncherProvider implements LauncherProvider { @Override public Launcher provide(Parameters parameters) { return new StandardLauncher(parameters); } } + } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 391aa702..96b422b2 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -57,7 +57,6 @@ package org.prismlauncher.launcher.impl.legacy; - import net.minecraft.Launcher; import javax.imageio.ImageIO; @@ -77,7 +76,7 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -public final class LegacyFrame extends Frame { +public final class LegacyFrame extends Frame /* TODO consider JFrame */ { private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); @@ -163,7 +162,7 @@ public final class LegacyFrame extends Frame { setResizable(true); if (maximize) - this.setExtendedState(MAXIMIZED_BOTH); + setExtendedState(MAXIMIZED_BOTH); validate(); diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 17cdeb08..71e4bc8d 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -56,7 +56,6 @@ package org.prismlauncher.launcher.impl.legacy; - import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.launcher.impl.AbstractLauncher; @@ -71,7 +70,6 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; - /** * Used to launch old versions that support applets. */ @@ -80,13 +78,9 @@ public final class LegacyLauncher extends AbstractLauncher { private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); private final String user, session; - private final String title; - private final String appletClass; - private final boolean usesApplet; - private final String cwd; public LegacyLauncher(Parameters params) { @@ -100,7 +94,6 @@ public final class LegacyLauncher extends AbstractLauncher { List traits = params.getList("traits", Collections.emptyList()); usesApplet = !traits.contains("noapplet"); - //noinspection AccessOfSystemProperties cwd = System.getProperty("user.dir"); } @@ -113,9 +106,9 @@ public final class LegacyLauncher extends AbstractLauncher { Class main = ClassLoader.getSystemClassLoader().loadClass(this.mainClassName); Field gameDirField = ReflectionUtils.getMinecraftGameDirField(main); - if (gameDirField == null) { + if (gameDirField == null) LOGGER.warning("Could not find Minecraft path field"); - } else { + else { gameDirField.setAccessible(true); gameDirField.set(null /* field is static, so instance is null */, new File(cwd)); } @@ -144,11 +137,11 @@ public final class LegacyLauncher extends AbstractLauncher { } } - private static class LegacyLauncherProvider implements LauncherProvider { @Override public Launcher provide(Parameters parameters) { return new LegacyLauncher(parameters); } } + } diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index b0eefec7..ad9e57fd 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -49,8 +49,8 @@ import java.lang.reflect.Modifier; import java.util.logging.Level; import java.util.logging.Logger; - public final class ReflectionUtils { + private static final Logger LOGGER = Logger.getLogger("ReflectionUtils"); private ReflectionUtils() { @@ -160,10 +160,9 @@ public final class ReflectionUtils { * @throws NoSuchMethodException If no method matching the correct signature can be found * @throws IllegalAccessException If method handles cannot access the entrypoint */ - public static MethodHandle findMainEntrypoint(String entrypointClassName) throws - ClassNotFoundException, - NoSuchMethodException, - IllegalAccessException { + public static MethodHandle findMainMethod(String entrypointClassName) + throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException { return findMainEntrypoint(ClassLoader.getSystemClassLoader().loadClass(entrypointClassName)); } + } diff --git a/libraries/launcher/org/prismlauncher/utils/StringUtils.java b/libraries/launcher/org/prismlauncher/utils/StringUtils.java index d470624c..a371b0cb 100644 --- a/libraries/launcher/org/prismlauncher/utils/StringUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/StringUtils.java @@ -36,8 +36,8 @@ package org.prismlauncher.utils; - public final class StringUtils { + private StringUtils() { } @@ -46,6 +46,7 @@ public final class StringUtils { if (splitPoint == -1) return null; - return new String[]{ input.substring(0, splitPoint), input.substring(splitPoint + 1) }; + return new String[] { input.substring(0, splitPoint), input.substring(splitPoint + 1) }; } + } From cae1ba7cd8d447bb15df5a820c5d8e3665bb489d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 17:04:16 +0000 Subject: [PATCH 023/277] Less needless verbosity, and return fallback Needlessly verbose commit message ik. Signed-off-by: TheKodeToad --- .../prismlauncher/launcher/impl/StandardLauncher.java | 4 ---- .../launcher/impl/legacy/LegacyLauncher.java | 10 ++++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index a3ea2f4a..485f65ee 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -67,8 +67,6 @@ import java.util.logging.Logger; public final class StandardLauncher extends AbstractLauncher { - private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); - public StandardLauncher(Parameters params) { super(params); } @@ -101,8 +99,6 @@ public final class StandardLauncher extends AbstractLauncher { launchParameters.add(serverPort); } - LOGGER.info("Launching minecraft using the main class entrypoint"); - MethodHandle method = ReflectionUtils.findMainMethod(this.mainClassName); method.invokeExact((Object[]) launchParameters.toArray(new String[0])); } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 71e4bc8d..00f55095 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -127,14 +127,12 @@ public final class LegacyLauncher extends AbstractLauncher { this.mcParams.contains("--demo") ); } catch (Throwable e) { - LOGGER.log(Level.SEVERE, "Running applet wrapper failed with exception", e); + LOGGER.log(Level.SEVERE, "Running applet wrapper failed with exception; falling back to main class", e); } - } else { - LOGGER.info("Launching legacy minecraft using the main class entrypoint"); - MethodHandle method = ReflectionUtils.findMainEntrypoint(main); - - method.invokeExact((Object[]) mcParams.toArray(new String[0])); } + + MethodHandle method = ReflectionUtils.findMainEntrypoint(main); + method.invokeExact((Object[]) mcParams.toArray(new String[0])); } private static class LegacyLauncherProvider implements LauncherProvider { From 779bc2c63df131fce0d230401a5a891eb2eb8794 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 17:11:14 +0000 Subject: [PATCH 024/277] Fix WrongMethodTypeException. Invoke exact don't work like that Signed-off-by: TheKodeToad --- .../org/prismlauncher/launcher/impl/StandardLauncher.java | 3 +-- .../prismlauncher/launcher/impl/legacy/LegacyLauncher.java | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 485f65ee..d5b7961a 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -63,7 +63,6 @@ import org.prismlauncher.utils.ReflectionUtils; import java.lang.invoke.MethodHandle; import java.util.ArrayList; import java.util.List; -import java.util.logging.Logger; public final class StandardLauncher extends AbstractLauncher { @@ -100,7 +99,7 @@ public final class StandardLauncher extends AbstractLauncher { } MethodHandle method = ReflectionUtils.findMainMethod(this.mainClassName); - method.invokeExact((Object[]) launchParameters.toArray(new String[0])); + method.invokeExact(launchParameters.toArray(new String[0])); } private static class StandardLauncherProvider implements LauncherProvider { diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 00f55095..b7109962 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -125,14 +125,14 @@ public final class LegacyLauncher extends AbstractLauncher { this.width, this.height, this.maximize, this.serverAddress, this.serverPort, this.mcParams.contains("--demo") - ); + ); } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Running applet wrapper failed with exception; falling back to main class", e); } } MethodHandle method = ReflectionUtils.findMainEntrypoint(main); - method.invokeExact((Object[]) mcParams.toArray(new String[0])); + method.invokeExact(mcParams.toArray(new String[0])); } private static class LegacyLauncherProvider implements LauncherProvider { From 5b9bfe8891f007a9dcec1a4754df6bc62e0396eb Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 17:30:13 +0000 Subject: [PATCH 025/277] Attempt to mimic clang-format Signed-off-by: TheKodeToad --- libraries/launcher/formatting-profile.xml | 399 ++++++++++++++++++ .../launcher/net/minecraft/Launcher.java | 65 ++- .../org/prismlauncher/EntryPoint.java | 35 +- .../launcher/LauncherFactory.java | 29 +- .../launcher/impl/AbstractLauncher.java | 9 +- .../launcher/impl/StandardLauncher.java | 15 +- .../launcher/impl/legacy/LegacyFrame.java | 40 +- .../launcher/impl/legacy/LegacyLauncher.java | 23 +- .../org/prismlauncher/utils/Parameters.java | 15 +- .../prismlauncher/utils/ReflectionUtils.java | 50 ++- .../org/prismlauncher/utils/StringUtils.java | 6 +- 11 files changed, 529 insertions(+), 157 deletions(-) create mode 100644 libraries/launcher/formatting-profile.xml diff --git a/libraries/launcher/formatting-profile.xml b/libraries/launcher/formatting-profile.xml new file mode 100644 index 00000000..bebc783e --- /dev/null +++ b/libraries/launcher/formatting-profile.xml @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 796e4829..ce9b59d4 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -68,7 +68,8 @@ import java.util.TreeMap; /** * WARNING: This class is reflectively accessed by legacy Forge versions. *

- * Changing field and method declarations without further testing is not recommended. + * Changing field and method declarations without further testing is not + * recommended. */ public final class Launcher extends Applet implements AppletStub { @@ -80,11 +81,10 @@ public final class Launcher extends Applet implements AppletStub { private final URL documentBase; private boolean active = false; - public Launcher(Applet applet) { - this(applet, null); - } + public Launcher(Applet applet) { this(applet, null); } - public Launcher(Applet applet, URL documentBase) { + public Launcher(Applet applet, URL documentBase) + { setLayout(new BorderLayout()); this.add(applet, "Center"); @@ -108,7 +108,8 @@ public final class Launcher extends Applet implements AppletStub { } } - public void replace(Applet applet) { + public void replace(Applet applet) + { wrappedApplet = applet; applet.setStub(this); @@ -127,17 +128,14 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public boolean isActive() { - return active; - } + public boolean isActive() { return active; } @Override - public URL getDocumentBase() { - return documentBase; - } + public URL getDocumentBase() { return documentBase; } @Override - public URL getCodeBase() { + public URL getCodeBase() + { try { // TODO: 2022-10-27 Can this be changed to https? return new URL("http://www.minecraft.net/game/"); @@ -147,7 +145,8 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public String getParameter(String name) { + public String getParameter(String name) + { String param = params.get(name); if (param != null) @@ -162,62 +161,54 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public void resize(int width, int height) { - wrappedApplet.resize(width, height); - } + public void resize(int width, int height) { wrappedApplet.resize(width, height); } @Override - public void resize(Dimension size) { - wrappedApplet.resize(size); - } + public void resize(Dimension size) { wrappedApplet.resize(size); } @Override - public void init() { + public void init() + { if (wrappedApplet != null) wrappedApplet.init(); } @Override - public void start() { + public void start() + { wrappedApplet.start(); active = true; } @Override - public void stop() { + public void stop() + { wrappedApplet.stop(); active = false; } @Override - public void destroy() { - wrappedApplet.destroy(); - } + public void destroy() { wrappedApplet.destroy(); } @Override - public void appletResize(int width, int height) { - wrappedApplet.resize(width, height); - } + public void appletResize(int width, int height) { wrappedApplet.resize(width, height); } @Override - public void setVisible(boolean visible) { + public void setVisible(boolean visible) + { super.setVisible(visible); wrappedApplet.setVisible(visible); } @Override - public void paint(Graphics graphics) { - } + public void paint(Graphics graphics) {} @Override - public void update(Graphics graphics) { - } + public void update(Graphics graphics) {} - public void setParameter(String name, String value) { - params.put(name, value); - } + public void setParameter(String name, String value) { params.put(name, value); } } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index be180d6a..88d5d8be 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -71,25 +71,25 @@ import java.util.logging.Logger; public final class EntryPoint { private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - private EntryPoint() { - } + private EntryPoint() {} - public static void main(String[] args) { + public static void main(String[] args) + { ExitCode exitCode = listen(); if (exitCode != ExitCode.NORMAL) { LOGGER.warning("Exiting with " + exitCode); - //noinspection CallToSystemExit + // noinspection CallToSystemExit System.exit(exitCode.numericalCode); } } - private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { + private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException + { if (input.isEmpty()) // TODO: 2022-11-01 Should we just ignore this? throw new ParseException("Unexpected empty string! You should not pass empty newlines to stdin."); - if ("launch".equalsIgnoreCase(input)) return PreLaunchAction.LAUNCH; else if ("abort".equalsIgnoreCase(input)) @@ -98,8 +98,8 @@ public final class EntryPoint { String[] pair = StringUtils.splitStringPair(' ', input); if (pair == null) throw new ParseException(String.format( - "Could not split input string '%s' by space. All input provided from stdin must be either 'launch', 'abort', or " + - "in the format '[param name] [param]'.", + "Could not split input string '%s' by space. All input provided from stdin must be either 'launch', 'abort', or " + + "in the format '[param name] [param]'.", input)); params.add(pair[0], pair[1]); @@ -108,7 +108,8 @@ public final class EntryPoint { } } - private static ExitCode listen() { + private static ExitCode listen() + { Parameters parameters = new Parameters(); PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; @@ -116,7 +117,7 @@ public final class EntryPoint { String line; while (preLaunchAction == PreLaunchAction.PROCEED) { - //noinspection NestedAssignment + // noinspection NestedAssignment if ((line = reader.readLine()) != null) preLaunchAction = parseLine(line, parameters); else @@ -157,23 +158,15 @@ public final class EntryPoint { } private enum PreLaunchAction { - PROCEED, - LAUNCH, - ABORT + PROCEED, LAUNCH, ABORT } private enum ExitCode { - NORMAL(0), - ABORT(1), - ERROR(2), - ILLEGAL_ARGUMENT(3), - REFLECTION_EXCEPTION(4); + NORMAL(0), ABORT(1), ERROR(2), ILLEGAL_ARGUMENT(3), REFLECTION_EXCEPTION(4); private final int numericalCode; - ExitCode(int numericalCode) { - this.numericalCode = numericalCode; - } + ExitCode(int numericalCode) { this.numericalCode = numericalCode; } } } diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java index 4844a774..1f7ea91b 100644 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java @@ -46,36 +46,31 @@ import org.prismlauncher.utils.Parameters; public final class LauncherFactory { - private LauncherFactory() { - } + private LauncherFactory() + {} - public static Launcher createLauncher(String launcherType, Parameters parameters) { - return createLauncher(LauncherType.valueOf(launcherType.toUpperCase()), parameters); - } + public static Launcher createLauncher(String launcherType, Parameters parameters) + { return createLauncher(LauncherType.valueOf(launcherType.toUpperCase()), parameters); } - public static Launcher createLauncher(LauncherType launcherType, Parameters parameters) { + public static Launcher createLauncher(LauncherType launcherType, Parameters parameters) + { LauncherProvider launcherProvider = launcherType.getLauncherProvider(); return launcherProvider.provide(parameters); } - public static Launcher createLauncher(Parameters parameters) { - return createLauncher(parameters.getString("launcher"), parameters); - } + public static Launcher createLauncher(Parameters parameters) + { return createLauncher(parameters.getString("launcher"), parameters); } public enum LauncherType { - STANDARD(StandardLauncher.getProvider()), - LEGACY(LegacyLauncher.getProvider()); + STANDARD(StandardLauncher.getProvider()), LEGACY(LegacyLauncher.getProvider()); private final LauncherProvider launcherProvider; - LauncherType(LauncherProvider launcherProvider) { - this.launcherProvider = launcherProvider; - } + LauncherType(LauncherProvider launcherProvider) { this.launcherProvider = launcherProvider; } + + public LauncherProvider getLauncherProvider() { return launcherProvider; } - public LauncherProvider getLauncherProvider() { - return launcherProvider; - } } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 8aec7c28..9eda8caf 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -80,7 +80,8 @@ public abstract class AbstractLauncher implements Launcher { protected final String mainClassName; - protected AbstractLauncher(Parameters params) { + protected AbstractLauncher(Parameters params) + { this.mcParams = Collections.unmodifiableList(params.getList("param", new ArrayList())); this.mainClassName = params.getString("mainClass", "net.minecraft.client.Minecraft"); @@ -99,10 +100,12 @@ public abstract class AbstractLauncher implements Launcher { this.width = Integer.parseInt(sizePair[0]); this.height = Integer.parseInt(sizePair[1]); } catch (NumberFormatException e) { - throw new ParseException(String.format("Could not parse window parameters from '%s'", windowParams), e); + throw new ParseException(String.format("Could not parse window parameters from '%s'", windowParams), + e); } } else { - throw new ParseException(String.format("Invalid window size parameters '%s'. Format: [height]x[width]", windowParams)); + throw new ParseException( + String.format("Invalid window size parameters '%s'. Format: [height]x[width]", windowParams)); } } else { this.width = DEFAULT_WINDOW_WIDTH; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index d5b7961a..e7b4599b 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -66,16 +66,13 @@ import java.util.List; public final class StandardLauncher extends AbstractLauncher { - public StandardLauncher(Parameters params) { - super(params); - } + public StandardLauncher(Parameters params) { super(params); } - public static LauncherProvider getProvider() { - return new StandardLauncherProvider(); - } + public static LauncherProvider getProvider() { return new StandardLauncherProvider(); } @Override - public void launch() throws Throwable { + public void launch() throws Throwable + { // window size, title and state // FIXME: there is no good way to maximize the minecraft window from here. @@ -104,9 +101,7 @@ public final class StandardLauncher extends AbstractLauncher { private static class StandardLauncherProvider implements LauncherProvider { @Override - public Launcher provide(Parameters parameters) { - return new StandardLauncher(parameters); - } + public Launcher provide(Parameters parameters) { return new StandardLauncher(parameters); } } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 96b422b2..3cc10238 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -82,7 +82,8 @@ public final class LegacyFrame extends Frame /* TODO consider JFrame */ { private final Launcher launcher; - public LegacyFrame(String title, Applet applet) { + public LegacyFrame(String title, Applet applet) + { super(title); launcher = new Launcher(applet); @@ -98,35 +99,24 @@ public final class LegacyFrame extends Frame /* TODO consider JFrame */ { addWindowListener(new ForceExitHandler()); } - public void start ( - String user, - String session, - int width, - int height, - boolean maximize, - String serverAddress, - String serverPort, - boolean isDemo - ) { - // Implements support for launching in to multiplayer on classic servers using a mpticket - // file generated by an external program and stored in the instance's root folder. + public void start(String user, String session, int width, int height, boolean maximize, String serverAddress, + String serverPort, boolean isDemo) + { + // Implements support for launching in to multiplayer on classic servers using a + // mpticket + // file generated by an external program and stored in the instance's root + // folder. - Path mpticketFile = - Paths.get(System.getProperty("user.dir"), "..", "mpticket"); + Path mpticketFile = Paths.get(System.getProperty("user.dir"), "..", "mpticket"); - Path mpticketFileCorrupt = - Paths.get(System.getProperty("user.dir"), "..", "mpticket.corrupt"); + Path mpticketFileCorrupt = Paths.get(System.getProperty("user.dir"), "..", "mpticket.corrupt"); if (Files.exists(mpticketFile)) { try { List lines = Files.readAllLines(mpticketFile, StandardCharsets.UTF_8); if (lines.size() < 3) { - Files.move( - mpticketFile, - mpticketFileCorrupt, - StandardCopyOption.REPLACE_EXISTING - ); + Files.move(mpticketFile, mpticketFileCorrupt, StandardCopyOption.REPLACE_EXISTING); LOGGER.warning("Mpticket file is corrupted!"); } else { @@ -175,10 +165,12 @@ public final class LegacyFrame extends Frame /* TODO consider JFrame */ { private final class ForceExitHandler extends WindowAdapter { @Override - public void windowClosing(WindowEvent event) { + public void windowClosing(WindowEvent event) + { new Thread(new Runnable() { @Override - public void run() { + public void run() + { try { Thread.sleep(30000L); } catch (InterruptedException e) { diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index b7109962..5395acec 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -83,7 +83,8 @@ public final class LegacyLauncher extends AbstractLauncher { private final boolean usesApplet; private final String cwd; - public LegacyLauncher(Parameters params) { + public LegacyLauncher(Parameters params) + { super(params); user = params.getString("userName"); @@ -97,12 +98,11 @@ public final class LegacyLauncher extends AbstractLauncher { cwd = System.getProperty("user.dir"); } - public static LauncherProvider getProvider() { - return new LegacyLauncherProvider(); - } + public static LauncherProvider getProvider() { return new LegacyLauncherProvider(); } @Override - public void launch() throws Throwable { + public void launch() throws Throwable + { Class main = ClassLoader.getSystemClassLoader().loadClass(this.mainClassName); Field gameDirField = ReflectionUtils.getMinecraftGameDirField(main); @@ -119,13 +119,8 @@ public final class LegacyLauncher extends AbstractLauncher { try { LegacyFrame window = new LegacyFrame(title, ReflectionUtils.createAppletClass(this.appletClass)); - window.start( - this.user, - this.session, - this.width, this.height, this.maximize, - this.serverAddress, this.serverPort, - this.mcParams.contains("--demo") - ); + window.start(this.user, this.session, this.width, this.height, this.maximize, this.serverAddress, + this.serverPort, this.mcParams.contains("--demo")); } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Running applet wrapper failed with exception; falling back to main class", e); } @@ -137,9 +132,7 @@ public final class LegacyLauncher extends AbstractLauncher { private static class LegacyLauncherProvider implements LauncherProvider { @Override - public Launcher provide(Parameters parameters) { - return new LegacyLauncher(parameters); - } + public Launcher provide(Parameters parameters) { return new LegacyLauncher(parameters); } } } diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index d019aa41..3378775f 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -68,7 +68,8 @@ public final class Parameters { private final Map> map = new HashMap<>(); - public void add(String key, String value) { + public void add(String key, String value) + { List params = map.get(key); if (params == null) { @@ -80,7 +81,8 @@ public final class Parameters { params.add(value); } - public List getList(String key) throws ParameterNotFoundException { + public List getList(String key) throws ParameterNotFoundException + { List params = map.get(key); if (params == null) @@ -89,7 +91,8 @@ public final class Parameters { return params; } - public List getList(String key, List def) { + public List getList(String key, List def) + { List params = map.get(key); if (params == null || params.isEmpty()) @@ -98,7 +101,8 @@ public final class Parameters { return params; } - public String getString(String key) throws ParameterNotFoundException { + public String getString(String key) throws ParameterNotFoundException + { List list = getList(key); if (list.isEmpty()) @@ -107,7 +111,8 @@ public final class Parameters { return list.get(0); } - public String getString(String key, String def) { + public String getString(String key, String def) + { List params = map.get(key); if (params == null || params.isEmpty()) diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index ad9e57fd..6e882387 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -38,7 +38,6 @@ package org.prismlauncher.utils; - import java.applet.Applet; import java.io.File; import java.lang.invoke.MethodHandle; @@ -53,8 +52,7 @@ public final class ReflectionUtils { private static final Logger LOGGER = Logger.getLogger("ReflectionUtils"); - private ReflectionUtils() { - } + private ReflectionUtils() {} /** * Instantiate an applet class by name @@ -65,13 +63,16 @@ public final class ReflectionUtils { * * @throws ClassNotFoundException if the provided class name cannot be found * @throws NoSuchMethodException if the no-args constructor cannot be found - * @throws IllegalAccessException if the constructor cannot be accessed via method handles + * @throws IllegalAccessException if the constructor cannot be accessed via + * method handles * @throws Throwable any exceptions from the class's constructor */ - public static Applet createAppletClass(String appletClassName) throws Throwable { + public static Applet createAppletClass(String appletClassName) throws Throwable + { Class appletClass = ClassLoader.getSystemClassLoader().loadClass(appletClassName); - MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, MethodType.methodType(void.class)); + MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, + MethodType.methodType(void.class)); return (Applet) appletConstructor.invoke(); } @@ -82,7 +83,8 @@ public final class ReflectionUtils { * * @return The found field. */ - public static Field getMinecraftGameDirField(Class minecraftMainClass) { + public static Field getMinecraftGameDirField(Class minecraftMainClass) + { LOGGER.fine("Resolving minecraft game directory field"); // Field we're looking for is always // private static File obfuscatedName = null; @@ -94,7 +96,6 @@ public final class ReflectionUtils { int fieldModifiers = field.getModifiers(); - // Must be static if (!Modifier.isStatic(fieldModifiers)) { LOGGER.log(Level.FINE, "Rejecting field {0} because it is not static", field.getName()); @@ -113,7 +114,8 @@ public final class ReflectionUtils { continue; } - LOGGER.log(Level.FINE, "Identified field {0} to match conditions for minecraft game directory field", field.getName()); + LOGGER.log(Level.FINE, "Identified field {0} to match conditions for minecraft game directory field", + field.getName()); return field; } @@ -124,8 +126,7 @@ public final class ReflectionUtils { /** * Resolve main entrypoint and returns method handle for it. *

- * Resolves a method that matches the following signature - * + * Resolves a method that matches the following signature * public static void main(String[] args) { *

* } @@ -135,34 +136,39 @@ public final class ReflectionUtils { * * @return The method handle for the resolved entrypoint * - * @throws NoSuchMethodException If no method matching the correct signature can be found + * @throws NoSuchMethodException If no method matching the correct signature + * can be found * @throws IllegalAccessException If method handles cannot access the entrypoint */ - public static MethodHandle findMainEntrypoint(Class entrypointClass) throws NoSuchMethodException, IllegalAccessException { - return MethodHandles.lookup().findStatic(entrypointClass, "main", MethodType.methodType(void.class, String[].class)); + public static MethodHandle findMainEntrypoint(Class entrypointClass) + throws NoSuchMethodException, IllegalAccessException + { + return MethodHandles.lookup().findStatic(entrypointClass, "main", + MethodType.methodType(void.class, String[].class)); } /** * Resolve main entrypoint and returns method handle for it. *

- * Resolves a method that matches the following signature - * + * Resolves a method that matches the following signature * public static void main(String[] args) { *

* } * * - * @param entrypointClassName The name of the entrypoint class to resolve the method from + * @param entrypointClassName The name of the entrypoint class to resolve the + * method from * * @return The method handle for the resolved entrypoint * - * @throws ClassNotFoundException If a class cannot be found with the provided name - * @throws NoSuchMethodException If no method matching the correct signature can be found + * @throws ClassNotFoundException If a class cannot be found with the provided + * name + * @throws NoSuchMethodException If no method matching the correct signature + * can be found * @throws IllegalAccessException If method handles cannot access the entrypoint */ public static MethodHandle findMainMethod(String entrypointClassName) - throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException { - return findMainEntrypoint(ClassLoader.getSystemClassLoader().loadClass(entrypointClassName)); - } + throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException + { return findMainEntrypoint(ClassLoader.getSystemClassLoader().loadClass(entrypointClassName)); } } diff --git a/libraries/launcher/org/prismlauncher/utils/StringUtils.java b/libraries/launcher/org/prismlauncher/utils/StringUtils.java index a371b0cb..08e6770e 100644 --- a/libraries/launcher/org/prismlauncher/utils/StringUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/StringUtils.java @@ -38,10 +38,10 @@ package org.prismlauncher.utils; public final class StringUtils { - private StringUtils() { - } + private StringUtils() {} - public static String[] splitStringPair(char splitChar, String input) { + public static String[] splitStringPair(char splitChar, String input) + { int splitPoint = input.indexOf(splitChar); if (splitPoint == -1) return null; From 404796d4b25902bf5312202e2ecbb97729b4c480 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 17:33:21 +0000 Subject: [PATCH 026/277] Sorry for reverting a lot but copying a list just hurts me Signed-off-by: TheKodeToad --- .../launcher/impl/AbstractLauncher.java | 2 +- .../launcher/impl/StandardLauncher.java | 20 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 9eda8caf..391f71a9 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -82,7 +82,7 @@ public abstract class AbstractLauncher implements Launcher { protected AbstractLauncher(Parameters params) { - this.mcParams = Collections.unmodifiableList(params.getList("param", new ArrayList())); + this.mcParams = params.getList("param", new ArrayList()); this.mainClassName = params.getString("mainClass", "net.minecraft.client.Minecraft"); this.serverAddress = params.getString("serverAddress", null); diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index e7b4599b..8ecfffb4 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -79,24 +79,22 @@ public final class StandardLauncher extends AbstractLauncher { // the following often breaks linux screen setups // mcparams.add("--fullscreen"); - List launchParameters = new ArrayList<>(this.mcParams); - if (!this.maximize) { - launchParameters.add("--width"); - launchParameters.add(Integer.toString(width)); - launchParameters.add("--height"); - launchParameters.add(Integer.toString(height)); + mcParams.add("--width"); + mcParams.add(Integer.toString(width)); + mcParams.add("--height"); + mcParams.add(Integer.toString(height)); } if (this.serverAddress != null) { - launchParameters.add("--server"); - launchParameters.add(serverAddress); - launchParameters.add("--port"); - launchParameters.add(serverPort); + mcParams.add("--server"); + mcParams.add(serverAddress); + mcParams.add("--port"); + mcParams.add(serverPort); } MethodHandle method = ReflectionUtils.findMainMethod(this.mainClassName); - method.invokeExact(launchParameters.toArray(new String[0])); + method.invokeExact(mcParams.toArray(new String[0])); } private static class StandardLauncherProvider implements LauncherProvider { From fb677a7489201163de7d8ad1960ba8404a8f8a10 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 17:45:37 +0000 Subject: [PATCH 027/277] So turns out we can have nice things Signed-off-by: TheKodeToad --- .../org/prismlauncher/launcher/impl/AbstractLauncher.java | 1 - .../org/prismlauncher/launcher/impl/StandardLauncher.java | 2 -- .../org/prismlauncher/launcher/impl/legacy/LegacyFrame.java | 4 +++- .../prismlauncher/launcher/impl/legacy/LegacyLauncher.java | 2 ++ 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 391f71a9..7677df56 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -62,7 +62,6 @@ import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.StringUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public abstract class AbstractLauncher implements Launcher { diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 8ecfffb4..40a35473 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -61,8 +61,6 @@ import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.ReflectionUtils; import java.lang.invoke.MethodHandle; -import java.util.ArrayList; -import java.util.List; public final class StandardLauncher extends AbstractLauncher { diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 3cc10238..8f468cf6 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -60,6 +60,8 @@ package org.prismlauncher.launcher.impl.legacy; import net.minecraft.Launcher; import javax.imageio.ImageIO; +import javax.swing.JFrame; + import java.applet.Applet; import java.awt.Dimension; import java.awt.Frame; @@ -76,7 +78,7 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -public final class LegacyFrame extends Frame /* TODO consider JFrame */ { +public final class LegacyFrame extends JFrame { private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 5395acec..4defa8f3 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -121,6 +121,8 @@ public final class LegacyLauncher extends AbstractLauncher { window.start(this.user, this.session, this.width, this.height, this.maximize, this.serverAddress, this.serverPort, this.mcParams.contains("--demo")); + + return; } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Running applet wrapper failed with exception; falling back to main class", e); } From 1da834f6507a8c494d38159208471afd5c9a877e Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 17:50:54 +0000 Subject: [PATCH 028/277] *opinionated change* Remove LauncherFactory Factories in OOP are a classic example of over-enginneering. When you only have two launchers I personally think that it's not very useful. Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 2 - .../org/prismlauncher/EntryPoint.java | 17 ++++- .../launcher/LauncherFactory.java | 76 ------------------- .../launcher/LauncherProvider.java | 46 ----------- .../launcher/impl/StandardLauncher.java | 9 --- .../launcher/impl/legacy/LegacyFrame.java | 1 - .../launcher/impl/legacy/LegacyLauncher.java | 9 --- 7 files changed, 15 insertions(+), 145 deletions(-) delete mode 100644 libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java delete mode 100644 libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 45a43b93..297ea27f 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -9,8 +9,6 @@ set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unche set(SRC org/prismlauncher/EntryPoint.java org/prismlauncher/launcher/Launcher.java - org/prismlauncher/launcher/LauncherFactory.java - org/prismlauncher/launcher/LauncherProvider.java org/prismlauncher/launcher/impl/legacy/LegacyFrame.java org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java org/prismlauncher/launcher/impl/AbstractLauncher.java diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 88d5d8be..c33ab983 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -57,7 +57,8 @@ package org.prismlauncher; import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; -import org.prismlauncher.launcher.LauncherFactory; +import org.prismlauncher.launcher.impl.StandardLauncher; +import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.StringUtils; @@ -137,7 +138,19 @@ public final class EntryPoint { } try { - Launcher launcher = LauncherFactory.createLauncher(parameters); + Launcher launcher; + String type = parameters.getString("launcher"); + + switch (type) { + case "standard": + launcher = new StandardLauncher(parameters); + break; + case "legacy": + launcher = new LegacyLauncher(parameters); + break; + default: + throw new IllegalArgumentException("Invalid launcher type: " + type); + } launcher.launch(); diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java b/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java deleted file mode 100644 index 1f7ea91b..00000000 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - * - * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Samisafool - * Copyright (C) 2022 TheKodeToad - * Copyright (C) 2022 solonovamax - * - * 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. - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole - * combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish to do - * so, delete this exception statement from your version. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.prismlauncher.launcher; - -import org.prismlauncher.launcher.impl.StandardLauncher; -import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; -import org.prismlauncher.utils.Parameters; - -public final class LauncherFactory { - - private LauncherFactory() - {} - - public static Launcher createLauncher(String launcherType, Parameters parameters) - { return createLauncher(LauncherType.valueOf(launcherType.toUpperCase()), parameters); } - - public static Launcher createLauncher(LauncherType launcherType, Parameters parameters) - { - LauncherProvider launcherProvider = launcherType.getLauncherProvider(); - - return launcherProvider.provide(parameters); - } - - public static Launcher createLauncher(Parameters parameters) - { return createLauncher(parameters.getString("launcher"), parameters); } - - public enum LauncherType { - STANDARD(StandardLauncher.getProvider()), LEGACY(LegacyLauncher.getProvider()); - - private final LauncherProvider launcherProvider; - - LauncherType(LauncherProvider launcherProvider) { this.launcherProvider = launcherProvider; } - - public LauncherProvider getLauncherProvider() { return launcherProvider; } - - } - -} diff --git a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java b/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java deleted file mode 100644 index 73d0cdc5..00000000 --- a/libraries/launcher/org/prismlauncher/launcher/LauncherProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - * - * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 solonovamax - * - * 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. - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole - * combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish to do - * so, delete this exception statement from your version. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.prismlauncher.launcher; - -import org.prismlauncher.utils.Parameters; - -public interface LauncherProvider { - - Launcher provide(Parameters parameters); - -} diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 40a35473..43d706d6 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -55,8 +55,6 @@ package org.prismlauncher.launcher.impl; -import org.prismlauncher.launcher.Launcher; -import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.ReflectionUtils; @@ -66,8 +64,6 @@ public final class StandardLauncher extends AbstractLauncher { public StandardLauncher(Parameters params) { super(params); } - public static LauncherProvider getProvider() { return new StandardLauncherProvider(); } - @Override public void launch() throws Throwable { @@ -95,9 +91,4 @@ public final class StandardLauncher extends AbstractLauncher { method.invokeExact(mcParams.toArray(new String[0])); } - private static class StandardLauncherProvider implements LauncherProvider { - @Override - public Launcher provide(Parameters parameters) { return new StandardLauncher(parameters); } - } - } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 8f468cf6..198fb61a 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -64,7 +64,6 @@ import javax.swing.JFrame; import java.applet.Applet; import java.awt.Dimension; -import java.awt.Frame; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 4defa8f3..aa899a61 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -56,8 +56,6 @@ package org.prismlauncher.launcher.impl.legacy; -import org.prismlauncher.launcher.Launcher; -import org.prismlauncher.launcher.LauncherProvider; import org.prismlauncher.launcher.impl.AbstractLauncher; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.ReflectionUtils; @@ -98,8 +96,6 @@ public final class LegacyLauncher extends AbstractLauncher { cwd = System.getProperty("user.dir"); } - public static LauncherProvider getProvider() { return new LegacyLauncherProvider(); } - @Override public void launch() throws Throwable { @@ -132,9 +128,4 @@ public final class LegacyLauncher extends AbstractLauncher { method.invokeExact(mcParams.toArray(new String[0])); } - private static class LegacyLauncherProvider implements LauncherProvider { - @Override - public Launcher provide(Parameters parameters) { return new LegacyLauncher(parameters); } - } - } From 9931c9a286c1746c1fb290da50ab31c9d7c7d228 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 18:11:42 +0000 Subject: [PATCH 029/277] Remove arguments being passed twice Passing the classpath into stdin has no effect. Java is already provided the classpath with -cp, which pretty much takes up the largest part of the arguments anyway, which leads me to wonder, what's the point of stdin arguments at all? Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 15 --------------- .../launcher/org/prismlauncher/EntryPoint.java | 2 +- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 5a5245ed..39a7198c 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -629,21 +629,6 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "sessionId " + session->session + "\n"; } - // libraries and class path. - { - QStringList jars, nativeJars; - profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot()); - for(auto file: jars) - { - launchScript += "cp " + file + "\n"; - } - for(auto file: nativeJars) - { - launchScript += "ext " + file + "\n"; - } - launchScript += "natives " + getNativePath() + "\n"; - } - for (auto trait : profile->getTraits()) { launchScript += "traits " + trait + "\n"; diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index c33ab983..36831179 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -81,7 +81,6 @@ public final class EntryPoint { if (exitCode != ExitCode.NORMAL) { LOGGER.warning("Exiting with " + exitCode); - // noinspection CallToSystemExit System.exit(exitCode.numericalCode); } } @@ -97,6 +96,7 @@ public final class EntryPoint { return PreLaunchAction.ABORT; else { String[] pair = StringUtils.splitStringPair(' ', input); + if (pair == null) throw new ParseException(String.format( "Could not split input string '%s' by space. All input provided from stdin must be either 'launch', 'abort', or " From 5912ef3b452b2c28693630249052fe23c790f6fb Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 18:17:11 +0000 Subject: [PATCH 030/277] Just ignore empty lines Signed-off-by: TheKodeToad --- libraries/launcher/org/prismlauncher/EntryPoint.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 36831179..06c6c79c 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -87,8 +87,8 @@ public final class EntryPoint { private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { - if (input.isEmpty()) // TODO: 2022-11-01 Should we just ignore this? - throw new ParseException("Unexpected empty string! You should not pass empty newlines to stdin."); + if (input.isEmpty()) + return PreLaunchAction.PROCEED; if ("launch".equalsIgnoreCase(input)) return PreLaunchAction.LAUNCH; From 4abf3a20c6fe3763e57b76ec873cc2355d067b90 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 3 Nov 2022 18:39:34 +0000 Subject: [PATCH 031/277] More standard code formatting profile Signed-off-by: TheKodeToad --- libraries/launcher/formatting-profile.xml | 6 +- .../launcher/net/minecraft/Launcher.java | 62 +++++++++++-------- .../org/prismlauncher/EntryPoint.java | 17 ++--- .../launcher/impl/AbstractLauncher.java | 3 +- .../launcher/impl/StandardLauncher.java | 7 ++- .../launcher/impl/legacy/LegacyFrame.java | 12 ++-- .../launcher/impl/legacy/LegacyLauncher.java | 6 +- .../org/prismlauncher/utils/Parameters.java | 15 ++--- .../prismlauncher/utils/ReflectionUtils.java | 17 +++-- .../org/prismlauncher/utils/StringUtils.java | 6 +- 10 files changed, 75 insertions(+), 76 deletions(-) diff --git a/libraries/launcher/formatting-profile.xml b/libraries/launcher/formatting-profile.xml index bebc783e..1b334838 100644 --- a/libraries/launcher/formatting-profile.xml +++ b/libraries/launcher/formatting-profile.xml @@ -246,11 +246,11 @@ - + - + @@ -262,7 +262,7 @@ - + diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index ce9b59d4..3bd38e77 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -81,10 +81,11 @@ public final class Launcher extends Applet implements AppletStub { private final URL documentBase; private boolean active = false; - public Launcher(Applet applet) { this(applet, null); } + public Launcher(Applet applet) { + this(applet, null); + } - public Launcher(Applet applet, URL documentBase) - { + public Launcher(Applet applet, URL documentBase) { setLayout(new BorderLayout()); this.add(applet, "Center"); @@ -108,8 +109,7 @@ public final class Launcher extends Applet implements AppletStub { } } - public void replace(Applet applet) - { + public void replace(Applet applet) { wrappedApplet = applet; applet.setStub(this); @@ -128,14 +128,17 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public boolean isActive() { return active; } + public boolean isActive() { + return active; + } @Override - public URL getDocumentBase() { return documentBase; } + public URL getDocumentBase() { + return documentBase; + } @Override - public URL getCodeBase() - { + public URL getCodeBase() { try { // TODO: 2022-10-27 Can this be changed to https? return new URL("http://www.minecraft.net/game/"); @@ -145,8 +148,7 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public String getParameter(String name) - { + public String getParameter(String name) { String param = params.get(name); if (param != null) @@ -161,54 +163,62 @@ public final class Launcher extends Applet implements AppletStub { } @Override - public void resize(int width, int height) { wrappedApplet.resize(width, height); } + public void resize(int width, int height) { + wrappedApplet.resize(width, height); + } @Override - public void resize(Dimension size) { wrappedApplet.resize(size); } + public void resize(Dimension size) { + wrappedApplet.resize(size); + } @Override - public void init() - { + public void init() { if (wrappedApplet != null) wrappedApplet.init(); } @Override - public void start() - { + public void start() { wrappedApplet.start(); active = true; } @Override - public void stop() - { + public void stop() { wrappedApplet.stop(); active = false; } @Override - public void destroy() { wrappedApplet.destroy(); } + public void destroy() { + wrappedApplet.destroy(); + } @Override - public void appletResize(int width, int height) { wrappedApplet.resize(width, height); } + public void appletResize(int width, int height) { + wrappedApplet.resize(width, height); + } @Override - public void setVisible(boolean visible) - { + public void setVisible(boolean visible) { super.setVisible(visible); wrappedApplet.setVisible(visible); } @Override - public void paint(Graphics graphics) {} + public void paint(Graphics graphics) { + } @Override - public void update(Graphics graphics) {} + public void update(Graphics graphics) { + } - public void setParameter(String name, String value) { params.put(name, value); } + public void setParameter(String name, String value) { + params.put(name, value); + } } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 06c6c79c..5332bfc7 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -72,10 +72,10 @@ import java.util.logging.Logger; public final class EntryPoint { private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - private EntryPoint() {} + private EntryPoint() { + } - public static void main(String[] args) - { + public static void main(String[] args) { ExitCode exitCode = listen(); if (exitCode != ExitCode.NORMAL) { @@ -85,8 +85,7 @@ public final class EntryPoint { } } - private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException - { + private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { if (input.isEmpty()) return PreLaunchAction.PROCEED; @@ -109,8 +108,7 @@ public final class EntryPoint { } } - private static ExitCode listen() - { + private static ExitCode listen() { Parameters parameters = new Parameters(); PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; @@ -179,7 +177,10 @@ public final class EntryPoint { private final int numericalCode; - ExitCode(int numericalCode) { this.numericalCode = numericalCode; } + ExitCode(int numericalCode) { + this.numericalCode = numericalCode; + } + } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 7677df56..938e3403 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -79,8 +79,7 @@ public abstract class AbstractLauncher implements Launcher { protected final String mainClassName; - protected AbstractLauncher(Parameters params) - { + protected AbstractLauncher(Parameters params) { this.mcParams = params.getList("param", new ArrayList()); this.mainClassName = params.getString("mainClass", "net.minecraft.client.Minecraft"); diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 43d706d6..0652cbae 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -62,11 +62,12 @@ import java.lang.invoke.MethodHandle; public final class StandardLauncher extends AbstractLauncher { - public StandardLauncher(Parameters params) { super(params); } + public StandardLauncher(Parameters params) { + super(params); + } @Override - public void launch() throws Throwable - { + public void launch() throws Throwable { // window size, title and state // FIXME: there is no good way to maximize the minecraft window from here. diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 198fb61a..d2496fa9 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -83,8 +83,7 @@ public final class LegacyFrame extends JFrame { private final Launcher launcher; - public LegacyFrame(String title, Applet applet) - { + public LegacyFrame(String title, Applet applet) { super(title); launcher = new Launcher(applet); @@ -101,8 +100,7 @@ public final class LegacyFrame extends JFrame { } public void start(String user, String session, int width, int height, boolean maximize, String serverAddress, - String serverPort, boolean isDemo) - { + String serverPort, boolean isDemo) { // Implements support for launching in to multiplayer on classic servers using a // mpticket // file generated by an external program and stored in the instance's root @@ -166,12 +164,10 @@ public final class LegacyFrame extends JFrame { private final class ForceExitHandler extends WindowAdapter { @Override - public void windowClosing(WindowEvent event) - { + public void windowClosing(WindowEvent event) { new Thread(new Runnable() { @Override - public void run() - { + public void run() { try { Thread.sleep(30000L); } catch (InterruptedException e) { diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index aa899a61..953f83d9 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -81,8 +81,7 @@ public final class LegacyLauncher extends AbstractLauncher { private final boolean usesApplet; private final String cwd; - public LegacyLauncher(Parameters params) - { + public LegacyLauncher(Parameters params) { super(params); user = params.getString("userName"); @@ -97,8 +96,7 @@ public final class LegacyLauncher extends AbstractLauncher { } @Override - public void launch() throws Throwable - { + public void launch() throws Throwable { Class main = ClassLoader.getSystemClassLoader().loadClass(this.mainClassName); Field gameDirField = ReflectionUtils.getMinecraftGameDirField(main); diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 3378775f..d019aa41 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -68,8 +68,7 @@ public final class Parameters { private final Map> map = new HashMap<>(); - public void add(String key, String value) - { + public void add(String key, String value) { List params = map.get(key); if (params == null) { @@ -81,8 +80,7 @@ public final class Parameters { params.add(value); } - public List getList(String key) throws ParameterNotFoundException - { + public List getList(String key) throws ParameterNotFoundException { List params = map.get(key); if (params == null) @@ -91,8 +89,7 @@ public final class Parameters { return params; } - public List getList(String key, List def) - { + public List getList(String key, List def) { List params = map.get(key); if (params == null || params.isEmpty()) @@ -101,8 +98,7 @@ public final class Parameters { return params; } - public String getString(String key) throws ParameterNotFoundException - { + public String getString(String key) throws ParameterNotFoundException { List list = getList(key); if (list.isEmpty()) @@ -111,8 +107,7 @@ public final class Parameters { return list.get(0); } - public String getString(String key, String def) - { + public String getString(String key, String def) { List params = map.get(key); if (params == null || params.isEmpty()) diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index 6e882387..99df70c3 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -52,7 +52,8 @@ public final class ReflectionUtils { private static final Logger LOGGER = Logger.getLogger("ReflectionUtils"); - private ReflectionUtils() {} + private ReflectionUtils() { + } /** * Instantiate an applet class by name @@ -67,8 +68,7 @@ public final class ReflectionUtils { * method handles * @throws Throwable any exceptions from the class's constructor */ - public static Applet createAppletClass(String appletClassName) throws Throwable - { + public static Applet createAppletClass(String appletClassName) throws Throwable { Class appletClass = ClassLoader.getSystemClassLoader().loadClass(appletClassName); MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, @@ -83,8 +83,7 @@ public final class ReflectionUtils { * * @return The found field. */ - public static Field getMinecraftGameDirField(Class minecraftMainClass) - { + public static Field getMinecraftGameDirField(Class minecraftMainClass) { LOGGER.fine("Resolving minecraft game directory field"); // Field we're looking for is always // private static File obfuscatedName = null; @@ -141,8 +140,7 @@ public final class ReflectionUtils { * @throws IllegalAccessException If method handles cannot access the entrypoint */ public static MethodHandle findMainEntrypoint(Class entrypointClass) - throws NoSuchMethodException, IllegalAccessException - { + throws NoSuchMethodException, IllegalAccessException { return MethodHandles.lookup().findStatic(entrypointClass, "main", MethodType.methodType(void.class, String[].class)); } @@ -168,7 +166,8 @@ public final class ReflectionUtils { * @throws IllegalAccessException If method handles cannot access the entrypoint */ public static MethodHandle findMainMethod(String entrypointClassName) - throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException - { return findMainEntrypoint(ClassLoader.getSystemClassLoader().loadClass(entrypointClassName)); } + throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException { + return findMainEntrypoint(ClassLoader.getSystemClassLoader().loadClass(entrypointClassName)); + } } diff --git a/libraries/launcher/org/prismlauncher/utils/StringUtils.java b/libraries/launcher/org/prismlauncher/utils/StringUtils.java index 08e6770e..a371b0cb 100644 --- a/libraries/launcher/org/prismlauncher/utils/StringUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/StringUtils.java @@ -38,10 +38,10 @@ package org.prismlauncher.utils; public final class StringUtils { - private StringUtils() {} + private StringUtils() { + } - public static String[] splitStringPair(char splitChar, String input) - { + public static String[] splitStringPair(char splitChar, String input) { int splitPoint = input.indexOf(splitChar); if (splitPoint == -1) return null; From 56d5035c63fc3146832b1159fe4686d552da763d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Nov 2022 10:31:31 +0000 Subject: [PATCH 032/277] Fix the warnings properly Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 2 +- libraries/launcher/org/prismlauncher/EntryPoint.java | 2 +- .../org/prismlauncher/launcher/impl/legacy/LegacyFrame.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 297ea27f..7aabefa1 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(Java 1.7 REQUIRED COMPONENTS Development) include(UseJava) set(CMAKE_JAVA_JAR_ENTRY_POINT org.prismlauncher.EntryPoint) -set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unchecked) +set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7) set(SRC org/prismlauncher/EntryPoint.java diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 5332bfc7..eab31cb1 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -70,6 +70,7 @@ import java.util.logging.Level; import java.util.logging.Logger; public final class EntryPoint { + private static final Logger LOGGER = Logger.getLogger("EntryPoint"); private EntryPoint() { @@ -116,7 +117,6 @@ public final class EntryPoint { String line; while (preLaunchAction == PreLaunchAction.PROCEED) { - // noinspection NestedAssignment if ((line = reader.readLine()) != null) preLaunchAction = parseLine(line, parameters); else diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index d2496fa9..5b43f7b2 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -80,6 +80,7 @@ import java.util.logging.Logger; public final class LegacyFrame extends JFrame { private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); + private static final long serialVersionUID = 1L; private final Launcher launcher; From 922220c11e3081c6d3aaa6c664379ea398f1d67f Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Nov 2022 11:00:20 +0000 Subject: [PATCH 033/277] More consistent logging text Signed-off-by: TheKodeToad --- libraries/launcher/org/prismlauncher/EntryPoint.java | 4 ++-- .../org/prismlauncher/launcher/impl/legacy/LegacyFrame.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index eab31cb1..875c14cd 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -130,7 +130,7 @@ public final class EntryPoint { // Main loop if (preLaunchAction == PreLaunchAction.ABORT) { - LOGGER.info("Launch aborted by the launcher."); + LOGGER.info("Launch aborted by the launcher"); return ExitCode.ABORT; } @@ -154,7 +154,7 @@ public final class EntryPoint { return ExitCode.NORMAL; } catch (IllegalArgumentException e) { - LOGGER.log(Level.SEVERE, "Wrong argument.", e); + LOGGER.log(Level.SEVERE, "Wrong argument", e); return ExitCode.ILLEGAL_ARGUMENT; } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 5b43f7b2..f27405b5 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -94,7 +94,7 @@ public final class LegacyFrame extends JFrame { try { setIconImage(ImageIO.read(new File("icon.png"))); } catch (IOException e) { - LOGGER.log(Level.WARNING, "Unable to read Minecraft icon!", e); + LOGGER.log(Level.WARNING, "Unable to read Minecraft icon", e); } addWindowListener(new ForceExitHandler()); From 4a2df30f92262304c63876c6428c81b70a2f4558 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Nov 2022 16:11:43 +0000 Subject: [PATCH 034/277] Try to use more standard exit codes Signed-off-by: TheKodeToad --- libraries/launcher/org/prismlauncher/EntryPoint.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 875c14cd..0fa3691d 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -160,7 +160,7 @@ public final class EntryPoint { } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { LOGGER.log(Level.SEVERE, "Caught reflection exception from launcher", e); - return ExitCode.REFLECTION_EXCEPTION; + return ExitCode.ERROR; } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Exception caught from launcher", e); @@ -173,7 +173,7 @@ public final class EntryPoint { } private enum ExitCode { - NORMAL(0), ABORT(1), ERROR(2), ILLEGAL_ARGUMENT(3), REFLECTION_EXCEPTION(4); + NORMAL(0), ABORT(1), ERROR(2), ILLEGAL_ARGUMENT(65); private final int numericalCode; From 1ea2e854156820ee5ba3c59d0c99fe4465566b9c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Nov 2022 16:29:19 +0000 Subject: [PATCH 035/277] Implicit is generally prefered Signed-off-by: TheKodeToad --- libraries/launcher/org/prismlauncher/EntryPoint.java | 2 +- .../launcher/org/prismlauncher/exception/ParseException.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 0fa3691d..512b01a9 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -157,7 +157,7 @@ public final class EntryPoint { LOGGER.log(Level.SEVERE, "Wrong argument", e); return ExitCode.ILLEGAL_ARGUMENT; - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + } catch (ReflectiveOperationException e) { LOGGER.log(Level.SEVERE, "Caught reflection exception from launcher", e); return ExitCode.ERROR; diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 2fd693e7..49f5b006 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -57,7 +57,6 @@ public final class ParseException extends IllegalArgumentException { } public ParseException() { - super(); } public static ParseException forInputString(String inputString) { From 745c331311837471971152ef75f3ac12809217b6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Nov 2022 17:10:45 +0000 Subject: [PATCH 036/277] Remove last case of printStackTrace Signed-off-by: TheKodeToad --- .../org/prismlauncher/launcher/impl/legacy/LegacyFrame.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index f27405b5..28f85aa2 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -172,7 +172,7 @@ public final class LegacyFrame extends JFrame { try { Thread.sleep(30000L); } catch (InterruptedException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "Thread interrupted", e); } LOGGER.info("Forcing exit!"); From e6dc34fe102f183e6857744283ee86c620091d08 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Nov 2022 17:18:17 +0000 Subject: [PATCH 037/277] More license fixing Signed-off-by: TheKodeToad --- .../exception/ParameterNotFoundException.java | 2 -- .../exception/ParseException.java | 2 -- .../org/prismlauncher/launcher/Launcher.java | 2 -- .../launcher/impl/AbstractLauncher.java | 1 - .../launcher/impl/legacy/LegacyFrame.java | 2 -- .../org/prismlauncher/utils/Parameters.java | 2 -- .../prismlauncher/utils/ReflectionUtils.java | 19 ++++++++++++++++++- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java index ad973ace..52c2a368 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java @@ -3,8 +3,6 @@ * Prism Launcher * * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Samisafool * Copyright (C) 2022 solonovamax * Copyright (C) 2022 TheKodeToad * diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 49f5b006..80709c56 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -3,8 +3,6 @@ * Prism Launcher * * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Samisafool * Copyright (C) 2022 solonovamax * Copyright (C) 2022 TheKodeToad * diff --git a/libraries/launcher/org/prismlauncher/launcher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java index d54cfd5c..1a6577e0 100644 --- a/libraries/launcher/org/prismlauncher/launcher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -3,8 +3,6 @@ * Prism Launcher * * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Samisafool * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax * diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 938e3403..7ae7568c 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -3,7 +3,6 @@ * Prism Launcher * * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax * diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 28f85aa2..15da0371 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -3,9 +3,7 @@ * Prism Launcher * * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 flow - * Copyright (C) 2022 Samisafool * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index d019aa41..8286bd70 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -3,8 +3,6 @@ * Prism Launcher * * Copyright (C) 2022 icelimetea - * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Samisafool * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax * diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index 99df70c3..1d2383a4 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -2,9 +2,9 @@ /* * Prism Launcher * + * Copyright (C) 2022 icelimetea * Copyright (C) 2022 solonovamax * Copyright (C) 2022 TheKodeToad - * Copyright (C) 2022 icelimetea * * 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 @@ -34,6 +34,23 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.prismlauncher.utils; From d90eff64d06a668e730e8af33cec325a5bac94de Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 4 Nov 2022 17:45:33 +0000 Subject: [PATCH 038/277] Add StringUtils to CMakeLists.txt Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 7aabefa1..d176b1d4 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -17,6 +17,7 @@ set(SRC org/prismlauncher/exception/ParseException.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java + org/prismlauncher/utils/StringUtils.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) From 50d40257fe8631338f9b6c278cc18c8ca9cab1a1 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 5 Nov 2022 09:10:52 +0000 Subject: [PATCH 039/277] Always use this for consistency Signed-off-by: TheKodeToad --- .../launcher/net/minecraft/Launcher.java | 36 ++++++------- .../launcher/impl/StandardLauncher.java | 18 +++---- .../launcher/impl/legacy/LegacyFrame.java | 54 +++++++++---------- .../launcher/impl/legacy/LegacyLauncher.java | 18 +++---- .../org/prismlauncher/utils/Parameters.java | 12 ++--- 5 files changed, 69 insertions(+), 69 deletions(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index 3bd38e77..b895d5b7 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -113,28 +113,28 @@ public final class Launcher extends Applet implements AppletStub { wrappedApplet = applet; applet.setStub(this); - applet.setSize(getWidth(), getHeight()); + applet.setSize(this.getWidth(), this.getHeight()); setLayout(new BorderLayout()); this.add(applet, "Center"); applet.init(); - active = true; + this.active = true; applet.start(); - validate(); + this.validate(); } @Override public boolean isActive() { - return active; + return this.active; } @Override public URL getDocumentBase() { - return documentBase; + return this.documentBase; } @Override @@ -149,7 +149,7 @@ public final class Launcher extends Applet implements AppletStub { @Override public String getParameter(String name) { - String param = params.get(name); + String param = this.params.get(name); if (param != null) return param; @@ -164,49 +164,49 @@ public final class Launcher extends Applet implements AppletStub { @Override public void resize(int width, int height) { - wrappedApplet.resize(width, height); + this.wrappedApplet.resize(width, height); } @Override public void resize(Dimension size) { - wrappedApplet.resize(size); + this.wrappedApplet.resize(size); } @Override public void init() { - if (wrappedApplet != null) - wrappedApplet.init(); + if (this.wrappedApplet != null) + this.wrappedApplet.init(); } @Override public void start() { - wrappedApplet.start(); + this.wrappedApplet.start(); - active = true; + this.active = true; } @Override public void stop() { - wrappedApplet.stop(); + this.wrappedApplet.stop(); - active = false; + this.active = false; } @Override public void destroy() { - wrappedApplet.destroy(); + this.wrappedApplet.destroy(); } @Override public void appletResize(int width, int height) { - wrappedApplet.resize(width, height); + this.wrappedApplet.resize(width, height); } @Override public void setVisible(boolean visible) { super.setVisible(visible); - wrappedApplet.setVisible(visible); + this.wrappedApplet.setVisible(visible); } @Override @@ -218,7 +218,7 @@ public final class Launcher extends Applet implements AppletStub { } public void setParameter(String name, String value) { - params.put(name, value); + this.params.put(name, value); } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 0652cbae..0f6fcf34 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -75,21 +75,21 @@ public final class StandardLauncher extends AbstractLauncher { // mcparams.add("--fullscreen"); if (!this.maximize) { - mcParams.add("--width"); - mcParams.add(Integer.toString(width)); - mcParams.add("--height"); - mcParams.add(Integer.toString(height)); + this.mcParams.add("--width"); + this.mcParams.add(Integer.toString(this.width)); + this.mcParams.add("--height"); + this.mcParams.add(Integer.toString(this.height)); } if (this.serverAddress != null) { - mcParams.add("--server"); - mcParams.add(serverAddress); - mcParams.add("--port"); - mcParams.add(serverPort); + this.mcParams.add("--server"); + this.mcParams.add(this.serverAddress); + this.mcParams.add("--port"); + this.mcParams.add(this.serverPort); } MethodHandle method = ReflectionUtils.findMainMethod(this.mainClassName); - method.invokeExact(mcParams.toArray(new String[0])); + method.invokeExact(this.mcParams.toArray(new String[0])); } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 15da0371..eafc984a 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -85,17 +85,17 @@ public final class LegacyFrame extends JFrame { public LegacyFrame(String title, Applet applet) { super(title); - launcher = new Launcher(applet); + this.launcher = new Launcher(applet); - applet.setStub(launcher); + applet.setStub(this.launcher); try { - setIconImage(ImageIO.read(new File("icon.png"))); + this.setIconImage(ImageIO.read(new File("icon.png"))); } catch (IOException e) { LOGGER.log(Level.WARNING, "Unable to read Minecraft icon", e); } - addWindowListener(new ForceExitHandler()); + this.addWindowListener(new ForceExitHandler()); } public void start(String user, String session, int width, int height, boolean maximize, String serverAddress, @@ -119,9 +119,9 @@ public final class LegacyFrame extends JFrame { LOGGER.warning("Mpticket file is corrupted!"); } else { // Assumes parameters are valid and in the correct order - launcher.setParameter("server", lines.get(0)); - launcher.setParameter("port", lines.get(1)); - launcher.setParameter("mppass", lines.get(2)); + this.launcher.setParameter("server", lines.get(0)); + this.launcher.setParameter("port", lines.get(1)); + this.launcher.setParameter("mppass", lines.get(2)); } } catch (IOException e) { LOGGER.log(Level.WARNING, "Unable to read mpticket file!", e); @@ -129,35 +129,35 @@ public final class LegacyFrame extends JFrame { } if (serverAddress != null) { - launcher.setParameter("server", serverAddress); - launcher.setParameter("port", serverPort); + this.launcher.setParameter("server", serverAddress); + this.launcher.setParameter("port", serverPort); } - launcher.setParameter("username", user); - launcher.setParameter("sessionid", session); - launcher.setParameter("stand-alone", "true"); // Show the quit button. TODO: why won't this work? - launcher.setParameter("haspaid", "true"); // Some old versions need this for world saves to work. - launcher.setParameter("demo", isDemo ? "true" : "false"); - launcher.setParameter("fullscreen", "false"); + this.launcher.setParameter("username", user); + this.launcher.setParameter("sessionid", session); + this.launcher.setParameter("stand-alone", "true"); // Show the quit button. TODO: why won't this work? + this.launcher.setParameter("haspaid", "true"); // Some old versions need this for world saves to work. + this.launcher.setParameter("demo", isDemo ? "true" : "false"); + this.launcher.setParameter("fullscreen", "false"); - add(launcher); + this.add(this.launcher); - launcher.setPreferredSize(new Dimension(width, height)); + this.launcher.setPreferredSize(new Dimension(width, height)); - pack(); + this.pack(); - setLocationRelativeTo(null); - setResizable(true); + this.setLocationRelativeTo(null); + this.setResizable(true); if (maximize) setExtendedState(MAXIMIZED_BOTH); - validate(); + this.validate(); - launcher.init(); - launcher.start(); + this.launcher.init(); + this.launcher.start(); - setVisible(true); + this.setVisible(true); } private final class ForceExitHandler extends WindowAdapter { @@ -179,9 +179,9 @@ public final class LegacyFrame extends JFrame { } }).start(); - if (launcher != null) { - launcher.stop(); - launcher.destroy(); + if (LegacyFrame.this.launcher != null) { + LegacyFrame.this.launcher.stop(); + LegacyFrame.this.launcher.destroy(); } // old minecraft versions can hang without this >_< diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 953f83d9..9f76944f 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -84,15 +84,15 @@ public final class LegacyLauncher extends AbstractLauncher { public LegacyLauncher(Parameters params) { super(params); - user = params.getString("userName"); - session = params.getString("sessionId"); - title = params.getString("windowTitle", "Minecraft"); - appletClass = params.getString("appletClass", "net.minecraft.client.MinecraftApplet"); + this.user = params.getString("userName"); + this.session = params.getString("sessionId"); + this.title = params.getString("windowTitle", "Minecraft"); + this.appletClass = params.getString("appletClass", "net.minecraft.client.MinecraftApplet"); List traits = params.getList("traits", Collections.emptyList()); - usesApplet = !traits.contains("noapplet"); + this.usesApplet = !traits.contains("noapplet"); - cwd = System.getProperty("user.dir"); + this.cwd = System.getProperty("user.dir"); } @Override @@ -104,14 +104,14 @@ public final class LegacyLauncher extends AbstractLauncher { LOGGER.warning("Could not find Minecraft path field"); else { gameDirField.setAccessible(true); - gameDirField.set(null /* field is static, so instance is null */, new File(cwd)); + gameDirField.set(null /* field is static, so instance is null */, new File(this.cwd)); } if (this.usesApplet) { LOGGER.info("Launching legacy minecraft using applet wrapper..."); try { - LegacyFrame window = new LegacyFrame(title, ReflectionUtils.createAppletClass(this.appletClass)); + LegacyFrame window = new LegacyFrame(this.title, ReflectionUtils.createAppletClass(this.appletClass)); window.start(this.user, this.session, this.width, this.height, this.maximize, this.serverAddress, this.serverPort, this.mcParams.contains("--demo")); @@ -123,7 +123,7 @@ public final class LegacyLauncher extends AbstractLauncher { } MethodHandle method = ReflectionUtils.findMainEntrypoint(main); - method.invokeExact(mcParams.toArray(new String[0])); + method.invokeExact(this.mcParams.toArray(new String[0])); } } diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 8286bd70..7af3c5e8 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -67,19 +67,19 @@ public final class Parameters { private final Map> map = new HashMap<>(); public void add(String key, String value) { - List params = map.get(key); + List params = this.map.get(key); if (params == null) { params = new ArrayList<>(); - map.put(key, params); + this.map.put(key, params); } params.add(value); } public List getList(String key) throws ParameterNotFoundException { - List params = map.get(key); + List params = this.map.get(key); if (params == null) throw ParameterNotFoundException.forParameterName(key); @@ -88,7 +88,7 @@ public final class Parameters { } public List getList(String key, List def) { - List params = map.get(key); + List params = this.map.get(key); if (params == null || params.isEmpty()) return def; @@ -97,7 +97,7 @@ public final class Parameters { } public String getString(String key) throws ParameterNotFoundException { - List list = getList(key); + List list = this.getList(key); if (list.isEmpty()) throw ParameterNotFoundException.forParameterName(key); @@ -106,7 +106,7 @@ public final class Parameters { } public String getString(String key, String def) { - List params = map.get(key); + List params = this.map.get(key); if (params == null || params.isEmpty()) return def; From f2ca9a6b319ad0ade04837f6830e682bc86c01a2 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 5 Nov 2022 17:14:12 +0000 Subject: [PATCH 040/277] Revert switch to JUL for better colours Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 2 + .../org/prismlauncher/EntryPoint.java | 30 ++-- .../launcher/impl/legacy/LegacyFrame.java | 16 +-- .../launcher/impl/legacy/LegacyLauncher.java | 12 +- .../prismlauncher/utils/ReflectionUtils.java | 17 +-- .../prismlauncher/utils/logging/Level.java | 54 +++++++ .../org/prismlauncher/utils/logging/Log.java | 134 ++++++++++++++++++ 7 files changed, 223 insertions(+), 42 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/utils/logging/Level.java create mode 100644 libraries/launcher/org/prismlauncher/utils/logging/Log.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index d176b1d4..90adcd3d 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -18,6 +18,8 @@ set(SRC org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java org/prismlauncher/utils/StringUtils.java + org/prismlauncher/utils/logging/Level.java + org/prismlauncher/utils/logging/Log.java net/minecraft/Launcher.java ) add_jar(NewLaunch ${SRC}) diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 512b01a9..34e65672 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -55,24 +55,21 @@ package org.prismlauncher; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.impl.StandardLauncher; import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.StringUtils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.logging.Level; -import java.util.logging.Logger; +import org.prismlauncher.utils.logging.Log; public final class EntryPoint { - private static final Logger LOGGER = Logger.getLogger("EntryPoint"); - private EntryPoint() { } @@ -80,7 +77,7 @@ public final class EntryPoint { ExitCode exitCode = listen(); if (exitCode != ExitCode.NORMAL) { - LOGGER.warning("Exiting with " + exitCode); + Log.fatal("Exiting with " + exitCode); System.exit(exitCode.numericalCode); } @@ -123,14 +120,14 @@ public final class EntryPoint { preLaunchAction = PreLaunchAction.ABORT; } } catch (IOException | ParseException e) { - LOGGER.log(Level.SEVERE, "Launcher abort due to exception", e); + Log.fatal("Launcher abort due to exception", e); return ExitCode.ILLEGAL_ARGUMENT; } // Main loop if (preLaunchAction == PreLaunchAction.ABORT) { - LOGGER.info("Launch aborted by the launcher"); + Log.fatal("Launch aborted by the launcher"); return ExitCode.ABORT; } @@ -150,19 +147,22 @@ public final class EntryPoint { throw new IllegalArgumentException("Invalid launcher type: " + type); } + Log.launcher("Using " + type + " launcher"); + Log.blankLine(); + launcher.launch(); return ExitCode.NORMAL; } catch (IllegalArgumentException e) { - LOGGER.log(Level.SEVERE, "Wrong argument", e); + Log.fatal("Wrong argument", e); return ExitCode.ILLEGAL_ARGUMENT; } catch (ReflectiveOperationException e) { - LOGGER.log(Level.SEVERE, "Caught reflection exception from launcher", e); + Log.fatal("Caught reflection exception from launcher", e); return ExitCode.ERROR; } catch (Throwable e) { - LOGGER.log(Level.SEVERE, "Exception caught from launcher", e); + Log.fatal("Exception caught from launcher", e); return ExitCode.ERROR; } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index eafc984a..8ff64ced 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -60,6 +60,8 @@ import net.minecraft.Launcher; import javax.imageio.ImageIO; import javax.swing.JFrame; +import org.prismlauncher.utils.logging.Log; + import java.applet.Applet; import java.awt.Dimension; import java.awt.event.WindowAdapter; @@ -72,12 +74,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; public final class LegacyFrame extends JFrame { - private static final Logger LOGGER = Logger.getLogger("LegacyFrame"); private static final long serialVersionUID = 1L; private final Launcher launcher; @@ -92,7 +91,7 @@ public final class LegacyFrame extends JFrame { try { this.setIconImage(ImageIO.read(new File("icon.png"))); } catch (IOException e) { - LOGGER.log(Level.WARNING, "Unable to read Minecraft icon", e); + Log.error("Unable to read Minecraft icon", e); } this.addWindowListener(new ForceExitHandler()); @@ -116,7 +115,7 @@ public final class LegacyFrame extends JFrame { if (lines.size() < 3) { Files.move(mpticketFile, mpticketFileCorrupt, StandardCopyOption.REPLACE_EXISTING); - LOGGER.warning("Mpticket file is corrupted!"); + Log.warning("Mpticket file is corrupted!"); } else { // Assumes parameters are valid and in the correct order this.launcher.setParameter("server", lines.get(0)); @@ -124,7 +123,7 @@ public final class LegacyFrame extends JFrame { this.launcher.setParameter("mppass", lines.get(2)); } } catch (IOException e) { - LOGGER.log(Level.WARNING, "Unable to read mpticket file!", e); + Log.error("Unable to read mpticket file", e); } } @@ -170,11 +169,10 @@ public final class LegacyFrame extends JFrame { try { Thread.sleep(30000L); } catch (InterruptedException e) { - LOGGER.log(Level.SEVERE, "Thread interrupted", e); + Log.error("Thread interrupted", e); } - LOGGER.info("Forcing exit!"); - + Log.warning("Forcing exit"); System.exit(0); } }).start(); diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 9f76944f..5ffa9ec5 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -59,22 +59,19 @@ package org.prismlauncher.launcher.impl.legacy; import org.prismlauncher.launcher.impl.AbstractLauncher; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.ReflectionUtils; +import org.prismlauncher.utils.logging.Log; import java.io.File; import java.lang.invoke.MethodHandle; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Used to launch old versions that support applets. */ public final class LegacyLauncher extends AbstractLauncher { - private static final Logger LOGGER = Logger.getLogger("LegacyLauncher"); - private final String user, session; private final String title; private final String appletClass; @@ -101,24 +98,23 @@ public final class LegacyLauncher extends AbstractLauncher { Field gameDirField = ReflectionUtils.getMinecraftGameDirField(main); if (gameDirField == null) - LOGGER.warning("Could not find Minecraft path field"); + Log.warning("Could not find Minecraft path field"); else { gameDirField.setAccessible(true); gameDirField.set(null /* field is static, so instance is null */, new File(this.cwd)); } if (this.usesApplet) { - LOGGER.info("Launching legacy minecraft using applet wrapper..."); + Log.launcher("Launching with applet wrapper..."); try { LegacyFrame window = new LegacyFrame(this.title, ReflectionUtils.createAppletClass(this.appletClass)); window.start(this.user, this.session, this.width, this.height, this.maximize, this.serverAddress, this.serverPort, this.mcParams.contains("--demo")); - return; } catch (Throwable e) { - LOGGER.log(Level.SEVERE, "Running applet wrapper failed with exception; falling back to main class", e); + Log.error("Running applet wrapper failed with exception; falling back to main class", e); } } diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index 1d2383a4..3b299615 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -62,13 +62,11 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.logging.Level; -import java.util.logging.Logger; + +import org.prismlauncher.utils.logging.Log; public final class ReflectionUtils { - private static final Logger LOGGER = Logger.getLogger("ReflectionUtils"); - private ReflectionUtils() { } @@ -101,7 +99,7 @@ public final class ReflectionUtils { * @return The found field. */ public static Field getMinecraftGameDirField(Class minecraftMainClass) { - LOGGER.fine("Resolving minecraft game directory field"); + Log.debug("Resolving minecraft game directory field"); // Field we're looking for is always // private static File obfuscatedName = null; for (Field field : minecraftMainClass.getDeclaredFields()) { @@ -114,24 +112,23 @@ public final class ReflectionUtils { // Must be static if (!Modifier.isStatic(fieldModifiers)) { - LOGGER.log(Level.FINE, "Rejecting field {0} because it is not static", field.getName()); + Log.debug("Rejecting field " + field.getName() + " because it is not static"); continue; } // Must be private if (!Modifier.isPrivate(fieldModifiers)) { - LOGGER.log(Level.FINE, "Rejecting field {0} because it is not private", field.getName()); + Log.debug("Rejecting field " + field.getName() + " because it is not private"); continue; } // Must not be final if (Modifier.isFinal(fieldModifiers)) { - LOGGER.log(Level.FINE, "Rejecting field {0} because it is final", field.getName()); + Log.debug("Rejecting field " + field.getName() + " because it is final"); continue; } - LOGGER.log(Level.FINE, "Identified field {0} to match conditions for minecraft game directory field", - field.getName()); + Log.debug("Identified field " + field.getName() + " to match conditions for minecraft game directory field"); return field; } diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Level.java b/libraries/launcher/org/prismlauncher/utils/logging/Level.java new file mode 100644 index 00000000..330cec28 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/logging/Level.java @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 TheKodeToad + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.utils.logging; + +public enum Level { + LAUNCHER("Launcher"), DEBUG("Debug"), INFO("Info"), MESSAGE("Message"), + WARNING("Warning"), ERROR("Error", true), FATAL("Fatal", true); + + String name; + boolean stderr; + + Level(String name) { + this(name, false); + } + + Level(String name, boolean stderr) { + this.name = name; + this.stderr = stderr; + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Log.java b/libraries/launcher/org/prismlauncher/utils/logging/Log.java new file mode 100644 index 00000000..e1961991 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/logging/Log.java @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 TheKodeToad + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.utils.logging; + +import java.io.PrintStream; + +/** + * Used to print messages with different levels used to colourise the output. + * Used instead of a logging framework, as the launcher knows how to parse these + * messages. + */ +public final class Log { + + private static final PrintStream ERROR_PREFIX = new PrintStream(System.err) { + @Override + public void println(String x) { + error(x); + } + + @Override + public void println(Object x) { + error(String.valueOf(x)); + } + }, FATAL_PREFIX = new PrintStream(System.err) { + @Override + public void println(String x) { + fatal(x); + } + + @Override + public void println(Object x) { + fatal(String.valueOf(x)); + } + }; + + private static final boolean DEBUG = Boolean.getBoolean("org.prismlauncher.debug"); + + private Log() { + } + + public static void blankLine() { + System.out.println(); + } + + public static void launcher(String message) { + log(message, Level.LAUNCHER); + } + + public static void error(String message) { + log(message, Level.ERROR); + } + + public static void debug(String message) { + if (!DEBUG) + return; + + log(message, Level.DEBUG); + } + + public static void info(String message) { + log(message, Level.INFO); + } + + public static void warning(String message) { + log(message, Level.WARNING); + } + + public static void error(String message, Throwable e) { + error(message); + e.printStackTrace(ERROR_PREFIX); + } + + public static void fatal(String message) { + log(message, Level.FATAL); + } + + public static void fatal(String message, Throwable e) { + fatal(message); + e.printStackTrace(FATAL_PREFIX); + } + + /** + * Logs a message with the prefix !![LEVEL]!. + * + * @param message The message + * @param level The level + */ + public static void log(String message, Level level) { + String prefix = "!![" + level.name + "]!"; + // prefix first line + message = prefix + message; + // prefix subsequent lines + message = message.replace("\n", "\n" + prefix); + + if (level.stderr) + System.err.println(message); + else + System.out.println(message); + } + +} From 32c2ad2bbd087b83fe5e1cfe03926410ec95bcca Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 8 Nov 2022 17:51:18 +0000 Subject: [PATCH 041/277] A lot more cleanup Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 25 +- launcher/minecraft/MinecraftInstance.h | 4 +- libraries/README.md | 66 +-- libraries/launcher/CMakeLists.txt | 4 +- libraries/launcher/formatting-profile.xml | 399 ------------------ .../launcher/net/minecraft/Launcher.java | 87 ++-- .../org/prismlauncher/EntryPoint.java | 105 +++-- .../exception/ParameterNotFoundException.java | 22 +- .../exception/ParseException.java | 26 +- .../org/prismlauncher/launcher/Launcher.java | 3 +- .../launcher/impl/AbstractLauncher.java | 48 +-- .../launcher/impl/StandardLauncher.java | 38 +- .../launcher/impl/legacy/LegacyFrame.java | 99 +++-- .../launcher/impl/legacy/LegacyLauncher.java | 53 +-- .../org/prismlauncher/utils/Parameters.java | 23 +- .../prismlauncher/utils/ReflectionUtils.java | 103 ++--- .../org/prismlauncher/utils/StringUtils.java | 11 +- .../prismlauncher/utils/logging/Level.java | 11 +- .../org/prismlauncher/utils/logging/Log.java | 52 +-- .../utils/logging/LogPrintStream.java | 99 +++++ 20 files changed, 417 insertions(+), 861 deletions(-) delete mode 100644 libraries/launcher/formatting-profile.xml create mode 100644 libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 39a7198c..a5b19f09 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield * Copyright (C) 2022 TheKodeToad @@ -438,6 +438,17 @@ QStringList MinecraftInstance::javaArguments() return args; } +QString MinecraftInstance::getLauncher() +{ + auto profile = m_components->getProfile(); + + // use legacy launcher if the traits are set + if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch")) + return "legacy"; + + return "standard"; +} + QMap MinecraftInstance::getVariables() { QMap out; @@ -634,15 +645,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "traits " + trait + "\n"; } - launchScript += "launcher "; - - // use legacy launcher if the traits are set - if (profile->getTraits().contains("legacyLaunch") || profile->getTraits().contains("alphaLaunch")) - launchScript += "legacy"; - else - launchScript += "standard"; - - launchScript += "\n"; + launchScript += "launcher " + getLauncher() + "\n"; // qDebug() << "Generated launch script:" << launchScript; return launchScript; @@ -779,6 +782,8 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr out << "Window size: " + QString::number(width) + " x " + QString::number(height); } out << ""; + out << "Launcher: " + getLauncher(); + out << ""; return out; } diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 1895d187..1bbd7b83 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -130,6 +131,7 @@ public: QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin); /// get arguments passed to java QStringList javaArguments(); + QString getLauncher(); /// get variables for launch command variable substitution/environment QMap getVariables() override; diff --git a/libraries/README.md b/libraries/README.md index 2971e32b..ac5a3618 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -42,19 +42,20 @@ MIT licensed. Java launcher part for Minecraft. -It: +It does the following: -- Starts a process -- Waits for a launch script on stdin -- Consumes the launch script you feed it -- Proceeds with launch when it gets the `launcher` command +- Waits for a launch script on stdin. +- Consumes the launch script you feed it. +- Proceeds with launch when it gets the `launcher` command. + +If "abort" is sent, the process will exit. This means the process is essentially idle until the final command is sent. You can, for example, attach a profiler before you send it. -A `legacy` and `standard` launchers are available. +The `standard` and `legacy` launchers are available. -- `legacy` is intended for use with Minecraft versions < 1.6 and is deprecated. - `standard` can handle launching any Minecraft version, at the cost of some extra features `legacy` enables (custom window icon and title). +- `legacy` is intended for use with Minecraft versions < 1.6 and is deprecated. Example (some parts have been censored): @@ -64,7 +65,7 @@ mainClass net.minecraft.launchwrapper.Launch param --username param CENSORED param --version -param MultiMC5 +param Prism Launcher param --gameDir param /home/peterix/minecraft/FTB/17ForgeTest/minecraft param --assetsDir @@ -81,57 +82,10 @@ param --userType param mojang param --tweakClass param cpw.mods.fml.common.launcher.FMLTweaker -windowTitle MultiMC: 172ForgeTest +windowTitle Prism Launcher: 172ForgeTest windowParams 854x480 userName CENSORED sessionId token:CENSORED:CENSORED -cp /home/peterix/minecraft/FTB/libraries/com/mojang/realms/1.3.5/realms-1.3.5.jar -cp /home/peterix/minecraft/FTB/libraries/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar -cp /home/peterix/minecraft/FTB/libraries/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar -cp /home/peterix/minecraft/FTB/libraries/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar -cp /home/peterix/minecraft/FTB/libraries/java3d/vecmath/1.3.1/vecmath-1.3.1.jar -cp /home/peterix/minecraft/FTB/libraries/net/sf/trove4j/trove4j/3.0.3/trove4j-3.0.3.jar -cp /home/peterix/minecraft/FTB/libraries/com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar -cp /home/peterix/minecraft/FTB/libraries/net/sf/jopt-simple/jopt-simple/4.5/jopt-simple-4.5.jar -cp /home/peterix/minecraft/FTB/libraries/com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar -cp /home/peterix/minecraft/FTB/libraries/com/paulscode/codecwav/20101023/codecwav-20101023.jar -cp /home/peterix/minecraft/FTB/libraries/com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar -cp /home/peterix/minecraft/FTB/libraries/com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar -cp /home/peterix/minecraft/FTB/libraries/com/paulscode/soundsystem/20120107/soundsystem-20120107.jar -cp /home/peterix/minecraft/FTB/libraries/io/netty/netty-all/4.0.10.Final/netty-all-4.0.10.Final.jar -cp /home/peterix/minecraft/FTB/libraries/com/google/guava/guava/16.0/guava-16.0.jar -cp /home/peterix/minecraft/FTB/libraries/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1.jar -cp /home/peterix/minecraft/FTB/libraries/commons-io/commons-io/2.4/commons-io-2.4.jar -cp /home/peterix/minecraft/FTB/libraries/commons-codec/commons-codec/1.9/commons-codec-1.9.jar -cp /home/peterix/minecraft/FTB/libraries/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar -cp /home/peterix/minecraft/FTB/libraries/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar -cp /home/peterix/minecraft/FTB/libraries/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar -cp /home/peterix/minecraft/FTB/libraries/com/mojang/authlib/1.5.16/authlib-1.5.16.jar -cp /home/peterix/minecraft/FTB/libraries/org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar -cp /home/peterix/minecraft/FTB/libraries/org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar -cp /home/peterix/minecraft/FTB/libraries/org/lwjgl/lwjgl/lwjgl/2.9.1/lwjgl-2.9.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/lwjgl/lwjgl/lwjgl_util/2.9.1/lwjgl_util-2.9.1.jar -cp /home/peterix/minecraft/FTB/libraries/tv/twitch/twitch/5.16/twitch-5.16.jar -cp /home/peterix/minecraft/FTB/libraries/net/minecraftforge/forge/1.7.10-10.13.0.1178/forge-1.7.10-10.13.0.1178.jar -cp /home/peterix/minecraft/FTB/libraries/net/minecraft/launchwrapper/1.9/launchwrapper-1.9.jar -cp /home/peterix/minecraft/FTB/libraries/org/ow2/asm/asm-all/4.1/asm-all-4.1.jar -cp /home/peterix/minecraft/FTB/libraries/com/typesafe/akka/akka-actor_2.11/2.3.3/akka-actor_2.11-2.3.3.jar -cp /home/peterix/minecraft/FTB/libraries/com/typesafe/config/1.2.1/config-1.2.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-actors-migration_2.11/1.1.0/scala-actors-migration_2.11-1.1.0.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-compiler/2.11.1/scala-compiler-2.11.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/plugins/scala-continuations-library_2.11/1.0.2/scala-continuations-library_2.11-1.0.2.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/plugins/scala-continuations-plugin_2.11.1/1.0.2/scala-continuations-plugin_2.11.1-1.0.2.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-library/2.11.1/scala-library-2.11.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-parser-combinators_2.11/1.0.1/scala-parser-combinators_2.11-1.0.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-reflect/2.11.1/scala-reflect-2.11.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-swing_2.11/1.0.1/scala-swing_2.11-1.0.1.jar -cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-xml_2.11/1.0.2/scala-xml_2.11-1.0.2.jar -cp /home/peterix/minecraft/FTB/libraries/lzma/lzma/0.0.1/lzma-0.0.1.jar -ext /home/peterix/minecraft/FTB/libraries/org/lwjgl/lwjgl/lwjgl-platform/2.9.1/lwjgl-platform-2.9.1-natives-linux.jar -ext /home/peterix/minecraft/FTB/libraries/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar -natives /home/peterix/minecraft/FTB/17ForgeTest/natives -cp /home/peterix/minecraft/FTB/versions/1.7.10/1.7.10.jar launcher standard ``` diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 90adcd3d..1d22a93e 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -9,10 +9,10 @@ set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7) set(SRC org/prismlauncher/EntryPoint.java org/prismlauncher/launcher/Launcher.java - org/prismlauncher/launcher/impl/legacy/LegacyFrame.java - org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java org/prismlauncher/launcher/impl/AbstractLauncher.java org/prismlauncher/launcher/impl/StandardLauncher.java + org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java + org/prismlauncher/launcher/impl/legacy/LegacyFrame.java org/prismlauncher/exception/ParameterNotFoundException.java org/prismlauncher/exception/ParseException.java org/prismlauncher/utils/Parameters.java diff --git a/libraries/launcher/formatting-profile.xml b/libraries/launcher/formatting-profile.xml deleted file mode 100644 index 1b334838..00000000 --- a/libraries/launcher/formatting-profile.xml +++ /dev/null @@ -1,399 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index b895d5b7..a721495a 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax @@ -62,8 +61,10 @@ import java.awt.Dimension; import java.awt.Graphics; import java.net.MalformedURLException; import java.net.URL; +import java.util.HashMap; import java.util.Map; -import java.util.TreeMap; + +import org.prismlauncher.utils.logging.Log; /** * WARNING: This class is reflectively accessed by legacy Forge versions. @@ -75,7 +76,7 @@ public final class Launcher extends Applet implements AppletStub { private static final long serialVersionUID = 1L; - private final Map params = new TreeMap<>(); + private final Map params = new HashMap<>(); private Applet wrappedApplet; private final URL documentBase; @@ -88,75 +89,75 @@ public final class Launcher extends Applet implements AppletStub { public Launcher(Applet applet, URL documentBase) { setLayout(new BorderLayout()); - this.add(applet, "Center"); + add(applet, "Center"); wrappedApplet = applet; try { - if (documentBase != null) { - this.documentBase = documentBase; - } else if (applet.getClass().getPackage().getName().startsWith("com.mojang.")) { - // Special case only for Classic versions - - // TODO: 2022-10-27 Can this be changed to https - this.documentBase = new URL("http", "www.minecraft.net", 80, "/game/"); - } else { - // TODO: 2022-10-27 Can this be changed to https? - this.documentBase = new URL("http://www.minecraft.net/game/"); + if (documentBase == null) { + if (applet.getClass().getPackage().getName().startsWith("com.mojang.")) { + // Special case only for Classic versions + documentBase = new URL("http", "www.minecraft.net", 80, "/game/"); + } else { + documentBase = new URL("http://www.minecraft.net/game/"); + } } } catch (MalformedURLException e) { - throw new RuntimeException(e); + // handle gracefully - it won't happen, but Java requires that it is caught + Log.error("Failed to parse document base URL", e); } + + this.documentBase = documentBase; } public void replace(Applet applet) { wrappedApplet = applet; applet.setStub(this); - applet.setSize(this.getWidth(), this.getHeight()); + applet.setSize(getWidth(), getHeight()); setLayout(new BorderLayout()); - this.add(applet, "Center"); + add(applet, "Center"); applet.init(); - this.active = true; + active = true; applet.start(); - this.validate(); + validate(); } @Override public boolean isActive() { - return this.active; + return active; } @Override public URL getDocumentBase() { - return this.documentBase; + return documentBase; } @Override public URL getCodeBase() { try { - // TODO: 2022-10-27 Can this be changed to https? return new URL("http://www.minecraft.net/game/"); } catch (MalformedURLException e) { - throw new RuntimeException(e); + Log.error("Failed to parse codebase URL", e); + return null; } } @Override - public String getParameter(String name) { - String param = this.params.get(name); + public String getParameter(String key) { + String param = params.get(key); if (param != null) return param; try { - return super.getParameter(name); - } catch (Exception ignored) { + return super.getParameter(key); + } catch (Throwable ignored) { } return null; @@ -164,49 +165,49 @@ public final class Launcher extends Applet implements AppletStub { @Override public void resize(int width, int height) { - this.wrappedApplet.resize(width, height); + wrappedApplet.resize(width, height); } @Override public void resize(Dimension size) { - this.wrappedApplet.resize(size); + wrappedApplet.resize(size); } @Override public void init() { - if (this.wrappedApplet != null) - this.wrappedApplet.init(); + if (wrappedApplet != null) + wrappedApplet.init(); } @Override public void start() { - this.wrappedApplet.start(); + wrappedApplet.start(); - this.active = true; + active = true; } @Override public void stop() { - this.wrappedApplet.stop(); + wrappedApplet.stop(); - this.active = false; + active = false; } @Override public void destroy() { - this.wrappedApplet.destroy(); + wrappedApplet.destroy(); } @Override public void appletResize(int width, int height) { - this.wrappedApplet.resize(width, height); + wrappedApplet.resize(width, height); } @Override public void setVisible(boolean visible) { super.setVisible(visible); - this.wrappedApplet.setVisible(visible); + wrappedApplet.setVisible(visible); } @Override @@ -217,8 +218,12 @@ public final class Launcher extends Applet implements AppletStub { public void update(Graphics graphics) { } - public void setParameter(String name, String value) { - this.params.put(name, value); + public void setParameter(String key, String value) { + params.put(key, value); + } + + public void setParameter(String key, boolean value) { + setParameter(key, value ? "true" : "false"); } } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 34e65672..f6567468 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax @@ -56,7 +55,6 @@ package org.prismlauncher; import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; @@ -70,63 +68,40 @@ import org.prismlauncher.utils.logging.Log; public final class EntryPoint { - private EntryPoint() { - } - public static void main(String[] args) { - ExitCode exitCode = listen(); + ExitCode code = listen(); - if (exitCode != ExitCode.NORMAL) { - Log.fatal("Exiting with " + exitCode); + if (code != ExitCode.NORMAL) { + Log.fatal("Exiting with " + code); - System.exit(exitCode.numericalCode); - } - } - - private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { - if (input.isEmpty()) - return PreLaunchAction.PROCEED; - - if ("launch".equalsIgnoreCase(input)) - return PreLaunchAction.LAUNCH; - else if ("abort".equalsIgnoreCase(input)) - return PreLaunchAction.ABORT; - else { - String[] pair = StringUtils.splitStringPair(' ', input); - - if (pair == null) - throw new ParseException(String.format( - "Could not split input string '%s' by space. All input provided from stdin must be either 'launch', 'abort', or " - + "in the format '[param name] [param]'.", - input)); - - params.add(pair[0], pair[1]); - - return PreLaunchAction.PROCEED; + System.exit(code.numeric); } } private static ExitCode listen() { - Parameters parameters = new Parameters(); - PreLaunchAction preLaunchAction = PreLaunchAction.PROCEED; + Parameters params = new Parameters(); + PreLaunchAction action = PreLaunchAction.PROCEED; try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8))) { String line; - while (preLaunchAction == PreLaunchAction.PROCEED) { + while (action == PreLaunchAction.PROCEED) { if ((line = reader.readLine()) != null) - preLaunchAction = parseLine(line, parameters); + action = parseLine(line, params); else - preLaunchAction = PreLaunchAction.ABORT; + action = PreLaunchAction.ABORT; } - } catch (IOException | ParseException e) { - Log.fatal("Launcher abort due to exception", e); + } catch (IllegalArgumentException e) { + Log.fatal("Aborting due to wrong argument", e); return ExitCode.ILLEGAL_ARGUMENT; + } catch (Throwable e) { + Log.fatal("Aborting due to exception", e); + + return ExitCode.ABORT; } - // Main loop - if (preLaunchAction == PreLaunchAction.ABORT) { + if (action == PreLaunchAction.ABORT) { Log.fatal("Launch aborted by the launcher"); return ExitCode.ABORT; @@ -134,33 +109,28 @@ public final class EntryPoint { try { Launcher launcher; - String type = parameters.getString("launcher"); + String type = params.getString("launcher"); switch (type) { case "standard": - launcher = new StandardLauncher(parameters); + launcher = new StandardLauncher(params); break; + case "legacy": - launcher = new LegacyLauncher(parameters); + launcher = new LegacyLauncher(params); break; + default: throw new IllegalArgumentException("Invalid launcher type: " + type); } - Log.launcher("Using " + type + " launcher"); - Log.blankLine(); - launcher.launch(); return ExitCode.NORMAL; } catch (IllegalArgumentException e) { - Log.fatal("Wrong argument", e); + Log.fatal("Illegal argument", e); return ExitCode.ILLEGAL_ARGUMENT; - } catch (ReflectiveOperationException e) { - Log.fatal("Caught reflection exception from launcher", e); - - return ExitCode.ERROR; } catch (Throwable e) { Log.fatal("Exception caught from launcher", e); @@ -168,6 +138,29 @@ public final class EntryPoint { } } + private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { + switch (input) { + case "": + break; + + case "launch": + return PreLaunchAction.LAUNCH; + + case "abort": + return PreLaunchAction.ABORT; + + default: + String[] pair = StringUtils.splitStringPair(' ', input); + + if (pair == null) + throw new ParseException(input, "[key] [value]"); + + params.add(pair[0], pair[1]); + } + + return PreLaunchAction.PROCEED; + } + private enum PreLaunchAction { PROCEED, LAUNCH, ABORT } @@ -175,10 +168,10 @@ public final class EntryPoint { private enum ExitCode { NORMAL(0), ABORT(1), ERROR(2), ILLEGAL_ARGUMENT(65); - private final int numericalCode; + private final int numeric; - ExitCode(int numericalCode) { - this.numericalCode = numericalCode; + ExitCode(int numeric) { + this.numeric = numeric; } } diff --git a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java index 52c2a368..524076ff 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParameterNotFoundException.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 solonovamax * Copyright (C) 2022 TheKodeToad @@ -42,23 +41,8 @@ public final class ParameterNotFoundException extends IllegalArgumentException { private static final long serialVersionUID = 1L; - public ParameterNotFoundException(String message, Throwable cause) { - super(message, cause); - } - - public ParameterNotFoundException(Throwable cause) { - super(cause); - } - - public ParameterNotFoundException(String message) { - super(message); - } - - public ParameterNotFoundException() { - } - - public static ParameterNotFoundException forParameterName(String parameterName) { - return new ParameterNotFoundException(String.format("Unknown parameter name '%s'", parameterName)); + public ParameterNotFoundException(String key) { + super(String.format("Required parameter '%s' was not found", key)); } } diff --git a/libraries/launcher/org/prismlauncher/exception/ParseException.java b/libraries/launcher/org/prismlauncher/exception/ParseException.java index 80709c56..4608fdd1 100644 --- a/libraries/launcher/org/prismlauncher/exception/ParseException.java +++ b/libraries/launcher/org/prismlauncher/exception/ParseException.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 solonovamax * Copyright (C) 2022 TheKodeToad @@ -42,27 +41,8 @@ public final class ParseException extends IllegalArgumentException { private static final long serialVersionUID = 1L; - public ParseException(String message) { - super(message); - } - - public ParseException(String message, Throwable cause) { - super(message, cause); - } - - public ParseException(Throwable cause) { - super(cause); - } - - public ParseException() { - } - - public static ParseException forInputString(String inputString) { - return new ParseException(String.format("Could not parse input string '%s'", inputString)); - } - - public static ParseException forInputString(String inputString, Throwable cause) { - return new ParseException(String.format("Could not parse input string '%s'", inputString), cause); + public ParseException(String input, String format) { + super(String.format("For input '%s' - should match '%s'", input, format)); } } diff --git a/libraries/launcher/org/prismlauncher/launcher/Launcher.java b/libraries/launcher/org/prismlauncher/launcher/Launcher.java index 1a6577e0..049a83d8 100644 --- a/libraries/launcher/org/prismlauncher/launcher/Launcher.java +++ b/libraries/launcher/org/prismlauncher/launcher/Launcher.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 7ae7568c..585d55f1 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax @@ -55,21 +54,20 @@ package org.prismlauncher.launcher.impl; +import java.util.ArrayList; +import java.util.List; + import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.StringUtils; -import java.util.ArrayList; -import java.util.List; - public abstract class AbstractLauncher implements Launcher { - private static final int DEFAULT_WINDOW_WIDTH = 854; - private static final int DEFAULT_WINDOW_HEIGHT = 480; + private static final int DEFAULT_WINDOW_WIDTH = 854, DEFAULT_WINDOW_HEIGHT = 480; // parameters, separated from ParamBucket - protected final List mcParams; + protected final List gameArgs; // secondary parameters protected final int width, height; @@ -79,34 +77,34 @@ public abstract class AbstractLauncher implements Launcher { protected final String mainClassName; protected AbstractLauncher(Parameters params) { - this.mcParams = params.getList("param", new ArrayList()); - this.mainClassName = params.getString("mainClass", "net.minecraft.client.Minecraft"); + gameArgs = params.getList("param", new ArrayList()); + mainClassName = params.getString("mainClass", "net.minecraft.client.Minecraft"); - this.serverAddress = params.getString("serverAddress", null); - this.serverPort = params.getString("serverPort", null); + serverAddress = params.getString("serverAddress", null); + serverPort = params.getString("serverPort", null); String windowParams = params.getString("windowParams", null); - this.maximize = "max".equalsIgnoreCase(windowParams); + if ("max".equals(windowParams) || windowParams == null) { + maximize = windowParams != null; + + width = DEFAULT_WINDOW_WIDTH; + height = DEFAULT_WINDOW_HEIGHT; + } else { + maximize = false; - if (windowParams != null && !"max".equalsIgnoreCase(windowParams)) { String[] sizePair = StringUtils.splitStringPair('x', windowParams); if (sizePair != null) { try { - this.width = Integer.parseInt(sizePair[0]); - this.height = Integer.parseInt(sizePair[1]); - } catch (NumberFormatException e) { - throw new ParseException(String.format("Could not parse window parameters from '%s'", windowParams), - e); + width = Integer.parseInt(sizePair[0]); + height = Integer.parseInt(sizePair[1]); + return; + } catch (NumberFormatException ignored) { } - } else { - throw new ParseException( - String.format("Invalid window size parameters '%s'. Format: [height]x[width]", windowParams)); } - } else { - this.width = DEFAULT_WINDOW_WIDTH; - this.height = DEFAULT_WINDOW_HEIGHT; + + throw new ParseException(windowParams, "[width]x[height]"); } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 0f6fcf34..9436ff15 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax @@ -55,11 +54,11 @@ package org.prismlauncher.launcher.impl; +import java.lang.invoke.MethodHandle; + import org.prismlauncher.utils.Parameters; import org.prismlauncher.utils.ReflectionUtils; -import java.lang.invoke.MethodHandle; - public final class StandardLauncher extends AbstractLauncher { public StandardLauncher(Parameters params) { @@ -69,27 +68,24 @@ public final class StandardLauncher extends AbstractLauncher { @Override public void launch() throws Throwable { // window size, title and state - - // FIXME: there is no good way to maximize the minecraft window from here. - // the following often breaks linux screen setups - // mcparams.add("--fullscreen"); - - if (!this.maximize) { - this.mcParams.add("--width"); - this.mcParams.add(Integer.toString(this.width)); - this.mcParams.add("--height"); - this.mcParams.add(Integer.toString(this.height)); + // FIXME doesn't support maximisation + if (!maximize) { + gameArgs.add("--width"); + gameArgs.add(Integer.toString(width)); + gameArgs.add("--height"); + gameArgs.add(Integer.toString(height)); } - if (this.serverAddress != null) { - this.mcParams.add("--server"); - this.mcParams.add(this.serverAddress); - this.mcParams.add("--port"); - this.mcParams.add(this.serverPort); + if (serverAddress != null) { + gameArgs.add("--server"); + gameArgs.add(serverAddress); + gameArgs.add("--port"); + gameArgs.add(serverPort); } - MethodHandle method = ReflectionUtils.findMainMethod(this.mainClassName); - method.invokeExact(this.mcParams.toArray(new String[0])); + // find and invoke the main method + MethodHandle method = ReflectionUtils.findMainMethod(mainClassName); + method.invokeExact(gameArgs.toArray(new String[0])); } } diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java index 8ff64ced..c215e7fe 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyFrame.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 flow * Copyright (C) 2022 TheKodeToad @@ -55,13 +54,6 @@ package org.prismlauncher.launcher.impl.legacy; -import net.minecraft.Launcher; - -import javax.imageio.ImageIO; -import javax.swing.JFrame; - -import org.prismlauncher.utils.logging.Log; - import java.applet.Applet; import java.awt.Dimension; import java.awt.event.WindowAdapter; @@ -75,6 +67,13 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.List; +import javax.imageio.ImageIO; +import javax.swing.JFrame; + +import org.prismlauncher.utils.logging.Log; + +import net.minecraft.Launcher; + public final class LegacyFrame extends JFrame { private static final long serialVersionUID = 1L; @@ -84,85 +83,85 @@ public final class LegacyFrame extends JFrame { public LegacyFrame(String title, Applet applet) { super(title); - this.launcher = new Launcher(applet); + launcher = new Launcher(applet); - applet.setStub(this.launcher); + applet.setStub(launcher); try { - this.setIconImage(ImageIO.read(new File("icon.png"))); + setIconImage(ImageIO.read(new File("icon.png"))); } catch (IOException e) { - Log.error("Unable to read Minecraft icon", e); + Log.error("Failed to read window icon", e); } - this.addWindowListener(new ForceExitHandler()); + addWindowListener(new ForceExitHandler()); } public void start(String user, String session, int width, int height, boolean maximize, String serverAddress, - String serverPort, boolean isDemo) { + String serverPort, boolean demo) { // Implements support for launching in to multiplayer on classic servers using a - // mpticket - // file generated by an external program and stored in the instance's root - // folder. + // mpticket file generated by an external program and stored in the instance's + // root folder. + Path instanceFolder = Paths.get(".."); + Path mpticket = instanceFolder.resolve("mpticket"); + Path mpticketCorrupt = instanceFolder.resolve("mpticket.corrupt"); - Path mpticketFile = Paths.get(System.getProperty("user.dir"), "..", "mpticket"); - - Path mpticketFileCorrupt = Paths.get(System.getProperty("user.dir"), "..", "mpticket.corrupt"); - - if (Files.exists(mpticketFile)) { + if (Files.exists(mpticket)) { try { - List lines = Files.readAllLines(mpticketFile, StandardCharsets.UTF_8); + List lines = Files.readAllLines(mpticket, StandardCharsets.UTF_8); if (lines.size() < 3) { - Files.move(mpticketFile, mpticketFileCorrupt, StandardCopyOption.REPLACE_EXISTING); + Files.move(mpticket, mpticketCorrupt, StandardCopyOption.REPLACE_EXISTING); - Log.warning("Mpticket file is corrupted!"); + Log.warning("mpticket file is corrupted"); } else { // Assumes parameters are valid and in the correct order - this.launcher.setParameter("server", lines.get(0)); - this.launcher.setParameter("port", lines.get(1)); - this.launcher.setParameter("mppass", lines.get(2)); + launcher.setParameter("server", lines.get(0)); + launcher.setParameter("port", lines.get(1)); + launcher.setParameter("mppass", lines.get(2)); } } catch (IOException e) { - Log.error("Unable to read mpticket file", e); + Log.error("Failed to read mpticket file", e); } } if (serverAddress != null) { - this.launcher.setParameter("server", serverAddress); - this.launcher.setParameter("port", serverPort); + launcher.setParameter("server", serverAddress); + launcher.setParameter("port", serverPort); } - this.launcher.setParameter("username", user); - this.launcher.setParameter("sessionid", session); - this.launcher.setParameter("stand-alone", "true"); // Show the quit button. TODO: why won't this work? - this.launcher.setParameter("haspaid", "true"); // Some old versions need this for world saves to work. - this.launcher.setParameter("demo", isDemo ? "true" : "false"); - this.launcher.setParameter("fullscreen", "false"); + launcher.setParameter("username", user); + launcher.setParameter("sessionid", session); + launcher.setParameter("stand-alone", true); // Show the quit button. TODO: why won't this work? + launcher.setParameter("haspaid", true); // Some old versions need this for world saves to work. + launcher.setParameter("demo", demo); + launcher.setParameter("fullscreen", false); - this.add(this.launcher); + add(launcher); - this.launcher.setPreferredSize(new Dimension(width, height)); + launcher.setPreferredSize(new Dimension(width, height)); - this.pack(); + pack(); - this.setLocationRelativeTo(null); - this.setResizable(true); + setLocationRelativeTo(null); + setResizable(true); if (maximize) setExtendedState(MAXIMIZED_BOTH); - this.validate(); + validate(); - this.launcher.init(); - this.launcher.start(); + launcher.init(); + launcher.start(); - this.setVisible(true); + setVisible(true); } private final class ForceExitHandler extends WindowAdapter { @Override public void windowClosing(WindowEvent event) { + // FIXME better solution + new Thread(new Runnable() { @Override public void run() { @@ -177,9 +176,9 @@ public final class LegacyFrame extends JFrame { } }).start(); - if (LegacyFrame.this.launcher != null) { - LegacyFrame.this.launcher.stop(); - LegacyFrame.this.launcher.destroy(); + if (launcher != null) { + launcher.stop(); + launcher.destroy(); } // old minecraft versions can hang without this >_< diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java index 5ffa9ec5..d349177b 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/legacy/LegacyLauncher.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 flow * Copyright (C) 2022 TheKodeToad @@ -56,17 +55,17 @@ package org.prismlauncher.launcher.impl.legacy; -import org.prismlauncher.launcher.impl.AbstractLauncher; -import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.ReflectionUtils; -import org.prismlauncher.utils.logging.Log; - import java.io.File; import java.lang.invoke.MethodHandle; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; +import org.prismlauncher.launcher.impl.AbstractLauncher; +import org.prismlauncher.utils.Parameters; +import org.prismlauncher.utils.ReflectionUtils; +import org.prismlauncher.utils.logging.Log; + /** * Used to launch old versions that support applets. */ @@ -75,51 +74,53 @@ public final class LegacyLauncher extends AbstractLauncher { private final String user, session; private final String title; private final String appletClass; - private final boolean usesApplet; - private final String cwd; + private final boolean useApplet; + private final String gameDir; public LegacyLauncher(Parameters params) { super(params); - this.user = params.getString("userName"); - this.session = params.getString("sessionId"); - this.title = params.getString("windowTitle", "Minecraft"); - this.appletClass = params.getString("appletClass", "net.minecraft.client.MinecraftApplet"); + user = params.getString("userName"); + session = params.getString("sessionId"); + title = params.getString("windowTitle", "Minecraft"); + appletClass = params.getString("appletClass", "net.minecraft.client.MinecraftApplet"); List traits = params.getList("traits", Collections.emptyList()); - this.usesApplet = !traits.contains("noapplet"); + useApplet = !traits.contains("noapplet"); - this.cwd = System.getProperty("user.dir"); + gameDir = System.getProperty("user.dir"); } @Override public void launch() throws Throwable { - Class main = ClassLoader.getSystemClassLoader().loadClass(this.mainClassName); - Field gameDirField = ReflectionUtils.getMinecraftGameDirField(main); + Class main = ClassLoader.getSystemClassLoader().loadClass(mainClassName); + Field gameDirField = ReflectionUtils.findMinecraftGameDirField(main); if (gameDirField == null) - Log.warning("Could not find Minecraft path field"); + Log.warning("Could not find Minecraft folder field"); else { gameDirField.setAccessible(true); - gameDirField.set(null /* field is static, so instance is null */, new File(this.cwd)); + gameDirField.set(null, new File(gameDir)); } - if (this.usesApplet) { - Log.launcher("Launching with applet wrapper..."); + if (useApplet) { + System.setProperty("minecraft.applet.TargetDirectory", gameDir); try { - LegacyFrame window = new LegacyFrame(this.title, ReflectionUtils.createAppletClass(this.appletClass)); + LegacyFrame window = new LegacyFrame(title, ReflectionUtils.createAppletClass(appletClass)); - window.start(this.user, this.session, this.width, this.height, this.maximize, this.serverAddress, - this.serverPort, this.mcParams.contains("--demo")); + window.start(user, session, width, height, maximize, serverAddress, serverPort, + gameArgs.contains("--demo")); return; } catch (Throwable e) { Log.error("Running applet wrapper failed with exception; falling back to main class", e); } } - MethodHandle method = ReflectionUtils.findMainEntrypoint(main); - method.invokeExact(this.mcParams.toArray(new String[0])); + // find and invoke the main method, this time without size parameters + // in all versions that support applets, these are ignored + MethodHandle method = ReflectionUtils.findMainMethod(main); + method.invokeExact(gameArgs.toArray(new String[0])); } } diff --git a/libraries/launcher/org/prismlauncher/utils/Parameters.java b/libraries/launcher/org/prismlauncher/utils/Parameters.java index 7af3c5e8..6365753e 100644 --- a/libraries/launcher/org/prismlauncher/utils/Parameters.java +++ b/libraries/launcher/org/prismlauncher/utils/Parameters.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 solonovamax @@ -55,40 +54,40 @@ package org.prismlauncher.utils; -import org.prismlauncher.exception.ParameterNotFoundException; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.prismlauncher.exception.ParameterNotFoundException; + public final class Parameters { private final Map> map = new HashMap<>(); public void add(String key, String value) { - List params = this.map.get(key); + List params = map.get(key); if (params == null) { params = new ArrayList<>(); - this.map.put(key, params); + map.put(key, params); } params.add(value); } public List getList(String key) throws ParameterNotFoundException { - List params = this.map.get(key); + List params = map.get(key); if (params == null) - throw ParameterNotFoundException.forParameterName(key); + throw new ParameterNotFoundException(key); return params; } public List getList(String key, List def) { - List params = this.map.get(key); + List params = map.get(key); if (params == null || params.isEmpty()) return def; @@ -97,16 +96,16 @@ public final class Parameters { } public String getString(String key) throws ParameterNotFoundException { - List list = this.getList(key); + List list = getList(key); if (list.isEmpty()) - throw ParameterNotFoundException.forParameterName(key); + throw new ParameterNotFoundException(key); return list.get(0); } public String getString(String key, String def) { - List params = this.map.get(key); + List params = map.get(key); if (params == null || params.isEmpty()) return def; diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index 3b299615..dd212ef9 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 icelimetea * Copyright (C) 2022 solonovamax * Copyright (C) 2022 TheKodeToad @@ -67,68 +66,57 @@ import org.prismlauncher.utils.logging.Log; public final class ReflectionUtils { - private ReflectionUtils() { - } + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + private static final ClassLoader LOADER = ClassLoader.getSystemClassLoader(); /** - * Instantiate an applet class by name + * Construct a Java applet by its class name. * - * @param appletClassName The name of the applet class to resolve - * - * @return The instantiated applet class - * - * @throws ClassNotFoundException if the provided class name cannot be found - * @throws NoSuchMethodException if the no-args constructor cannot be found - * @throws IllegalAccessException if the constructor cannot be accessed via - * method handles - * @throws Throwable any exceptions from the class's constructor + * @param clazz The class name + * @return The applet instance + * @throws Throwable */ - public static Applet createAppletClass(String appletClassName) throws Throwable { - Class appletClass = ClassLoader.getSystemClassLoader().loadClass(appletClassName); + public static Applet createAppletClass(String clazz) throws Throwable { + Class appletClass = LOADER.loadClass(clazz); - MethodHandle appletConstructor = MethodHandles.lookup().findConstructor(appletClass, - MethodType.methodType(void.class)); + MethodHandle appletConstructor = LOOKUP.findConstructor(appletClass, MethodType.methodType(void.class)); return (Applet) appletConstructor.invoke(); } /** - * Finds a field that looks like a Minecraft base folder in a supplied class + * Best guess of the game directory field within net.minecraft.client.Minecraft. + * Designed for legacy versions - newer versions do not use a static field. * - * @param minecraftMainClass the class to scan - * - * @return The found field. + * @param clazz The class + * @return The first field matching criteria */ - public static Field getMinecraftGameDirField(Class minecraftMainClass) { + public static Field findMinecraftGameDirField(Class clazz) { Log.debug("Resolving minecraft game directory field"); - // Field we're looking for is always - // private static File obfuscatedName = null; - for (Field field : minecraftMainClass.getDeclaredFields()) { - // Has to be File + + // search for private static File + for (Field field : clazz.getDeclaredFields()) { if (field.getType() != File.class) { continue; } int fieldModifiers = field.getModifiers(); - // Must be static if (!Modifier.isStatic(fieldModifiers)) { Log.debug("Rejecting field " + field.getName() + " because it is not static"); continue; } - // Must be private if (!Modifier.isPrivate(fieldModifiers)) { Log.debug("Rejecting field " + field.getName() + " because it is not private"); continue; } - // Must not be final if (Modifier.isFinal(fieldModifiers)) { Log.debug("Rejecting field " + field.getName() + " because it is final"); continue; } - Log.debug("Identified field " + field.getName() + " to match conditions for minecraft game directory field"); + Log.debug("Identified field " + field.getName() + " to match conditions for game directory field"); return field; } @@ -137,51 +125,30 @@ public final class ReflectionUtils { } /** - * Resolve main entrypoint and returns method handle for it. - *

- * Resolves a method that matches the following signature - * public static void main(String[] args) { - *

- * } - * + * Gets the main method within a class. * - * @param entrypointClass The entrypoint class to resolve the method from - * - * @return The method handle for the resolved entrypoint - * - * @throws NoSuchMethodException If no method matching the correct signature - * can be found - * @throws IllegalAccessException If method handles cannot access the entrypoint + * @param clazz The class + * @return A method matching the descriptor of a main method + * @throws ClassNotFoundException + * @throws NoSuchMethodException + * @throws IllegalAccessException */ - public static MethodHandle findMainEntrypoint(Class entrypointClass) - throws NoSuchMethodException, IllegalAccessException { - return MethodHandles.lookup().findStatic(entrypointClass, "main", - MethodType.methodType(void.class, String[].class)); + public static MethodHandle findMainMethod(Class clazz) throws NoSuchMethodException, IllegalAccessException { + return LOOKUP.findStatic(clazz, "main", MethodType.methodType(void.class, String[].class)); } /** - * Resolve main entrypoint and returns method handle for it. - *

- * Resolves a method that matches the following signature - * public static void main(String[] args) { - *

- * } - * + * Gets the main method within a class by its name. * - * @param entrypointClassName The name of the entrypoint class to resolve the - * method from - * - * @return The method handle for the resolved entrypoint - * - * @throws ClassNotFoundException If a class cannot be found with the provided - * name - * @throws NoSuchMethodException If no method matching the correct signature - * can be found - * @throws IllegalAccessException If method handles cannot access the entrypoint + * @param clazz The class name + * @return A method matching the descriptor of a main method + * @throws ClassNotFoundException + * @throws NoSuchMethodException + * @throws IllegalAccessException */ - public static MethodHandle findMainMethod(String entrypointClassName) + public static MethodHandle findMainMethod(String clazz) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException { - return findMainEntrypoint(ClassLoader.getSystemClassLoader().loadClass(entrypointClassName)); + return findMainMethod(LOADER.loadClass(clazz)); } } diff --git a/libraries/launcher/org/prismlauncher/utils/StringUtils.java b/libraries/launcher/org/prismlauncher/utils/StringUtils.java index a371b0cb..dfd1634b 100644 --- a/libraries/launcher/org/prismlauncher/utils/StringUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/StringUtils.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * Prism Launcher - * + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 solonovamax * * This program is free software: you can redistribute it and/or modify @@ -38,15 +37,13 @@ package org.prismlauncher.utils; public final class StringUtils { - private StringUtils() { - } + public static String[] splitStringPair(char splitChar, String str) { + int splitPoint = str.indexOf(splitChar); - public static String[] splitStringPair(char splitChar, String input) { - int splitPoint = input.indexOf(splitChar); if (splitPoint == -1) return null; - return new String[] { input.substring(0, splitPoint), input.substring(splitPoint + 1) }; + return new String[] { str.substring(0, splitPoint), str.substring(splitPoint + 1) }; } } diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Level.java b/libraries/launcher/org/prismlauncher/utils/logging/Level.java index 330cec28..552b0b55 100644 --- a/libraries/launcher/org/prismlauncher/utils/logging/Level.java +++ b/libraries/launcher/org/prismlauncher/utils/logging/Level.java @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify @@ -36,8 +36,13 @@ package org.prismlauncher.utils.logging; public enum Level { - LAUNCHER("Launcher"), DEBUG("Debug"), INFO("Info"), MESSAGE("Message"), - WARNING("Warning"), ERROR("Error", true), FATAL("Fatal", true); + LAUNCHER("Launcher"), + DEBUG("Debug"), + INFO("Info"), + MESSAGE("Message"), + WARNING("Warning"), + ERROR("Error", true), + FATAL("Fatal", true); String name; boolean stderr; diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Log.java b/libraries/launcher/org/prismlauncher/utils/logging/Log.java index e1961991..374a8107 100644 --- a/libraries/launcher/org/prismlauncher/utils/logging/Log.java +++ b/libraries/launcher/org/prismlauncher/utils/logging/Log.java @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify @@ -44,37 +44,12 @@ import java.io.PrintStream; */ public final class Log { - private static final PrintStream ERROR_PREFIX = new PrintStream(System.err) { - @Override - public void println(String x) { - error(x); - } - - @Override - public void println(Object x) { - error(String.valueOf(x)); - } - }, FATAL_PREFIX = new PrintStream(System.err) { - @Override - public void println(String x) { - fatal(x); - } - - @Override - public void println(Object x) { - fatal(String.valueOf(x)); - } - }; - + // original before overridden + private static final PrintStream OUT = new PrintStream(System.out), ERR = new PrintStream(System.err); + private static final PrintStream ERROR_PREFIX = new LogPrintStream(System.err, Level.ERROR), + FATAL_PREFIX = new LogPrintStream(System.err, Level.FATAL); private static final boolean DEBUG = Boolean.getBoolean("org.prismlauncher.debug"); - private Log() { - } - - public static void blankLine() { - System.out.println(); - } - public static void launcher(String message) { log(message, Level.LAUNCHER); } @@ -84,16 +59,9 @@ public final class Log { } public static void debug(String message) { - if (!DEBUG) - return; - log(message, Level.DEBUG); } - public static void info(String message) { - log(message, Level.INFO); - } - public static void warning(String message) { log(message, Level.WARNING); } @@ -113,12 +81,16 @@ public final class Log { } /** - * Logs a message with the prefix !![LEVEL]!. + * Logs a message with the prefix !![LEVEL]!. This is picked up by + * the log viewer to give it nice colours. * * @param message The message * @param level The level */ public static void log(String message, Level level) { + if (!DEBUG && level == Level.DEBUG) + return; + String prefix = "!![" + level.name + "]!"; // prefix first line message = prefix + message; @@ -126,9 +98,9 @@ public final class Log { message = message.replace("\n", "\n" + prefix); if (level.stderr) - System.err.println(message); + ERR.println(message); else - System.out.println(message); + OUT.println(message); } } diff --git a/libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java b/libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java new file mode 100644 index 00000000..8a182817 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 TheKodeToad + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.utils.logging; + +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * Used to create a print stream that redirects to Log. + */ +final class LogPrintStream extends PrintStream { + + private final Level level; + + public LogPrintStream(OutputStream out, Level level) { + super(out); + + this.level = level; + } + + @Override + public void println(String x) { + Log.log(x, level); + } + + @Override + public void println(Object x) { + println(String.valueOf(x)); + } + + @Override + public void println(boolean x) { + println(String.valueOf(x)); + } + + @Override + public void println(char x) { + println(String.valueOf(x)); + } + + @Override + public void println(int x) { + println(String.valueOf(x)); + } + + @Override + public void println(long x) { + println(String.valueOf(x)); + } + + @Override + public void println(float x) { + println(String.valueOf(x)); + } + + @Override + public void println(double x) { + println(String.valueOf(x)); + } + + @Override + public void println(char[] x) { + println(String.valueOf(x)); + } + +} \ No newline at end of file From 5ee4fb3522ea2bb66ad49e1cdd91140b8d5c6a00 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 11 Nov 2022 15:02:08 +0100 Subject: [PATCH 042/277] feat: validate maximum memory allocation Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/global/JavaPage.cpp | 25 +++- launcher/ui/pages/global/JavaPage.h | 3 + launcher/ui/pages/global/JavaPage.ui | 54 ++++---- .../pages/instance/InstanceSettingsPage.cpp | 26 +++- .../ui/pages/instance/InstanceSettingsPage.h | 3 + .../ui/pages/instance/InstanceSettingsPage.ui | 119 ++++++++++-------- 6 files changed, 151 insertions(+), 79 deletions(-) diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 2cee15bf..00c06cff 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -58,9 +58,8 @@ JavaPage::JavaPage(QWidget *parent) : QWidget(parent), ui(new Ui::JavaPage) ui->setupUi(this); ui->tabWidget->tabBar()->hide(); - auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; - ui->maxMemSpinBox->setMaximum(sysMiB); loadSettings(); + updateThresholds(); } JavaPage::~JavaPage() @@ -177,6 +176,11 @@ void JavaPage::on_javaTestBtn_clicked() checker->run(); } +void JavaPage::on_maxMemSpinBox_valueChanged(int i) +{ + updateThresholds(); +} + void JavaPage::checkerFinished() { checker.reset(); @@ -186,3 +190,20 @@ void JavaPage::retranslate() { ui->retranslateUi(this); } + +void JavaPage::updateThresholds() +{ + auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; + unsigned int maxMem = ui->maxMemSpinBox->value(); + + if (maxMem >= sysMiB) { + ui->labelMaxMemIcon->setText(u8"✘"); + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity.")); + } else if (maxMem > (sysMiB * 0.9)) { + ui->labelMaxMemIcon->setText(u8"⚠"); + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); + } else { + ui->labelMaxMemIcon->setText(u8"✔"); + ui->labelMaxMemIcon->setToolTip(""); + } +} diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h index 64d4098e..2ef6d749 100644 --- a/launcher/ui/pages/global/JavaPage.h +++ b/launcher/ui/pages/global/JavaPage.h @@ -76,6 +76,8 @@ public: bool apply() override; void retranslate() override; + void updateThresholds(); + private: void applySettings(); void loadSettings(); @@ -85,6 +87,7 @@ slots: void on_javaDetectBtn_clicked(); void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); + void on_maxMemSpinBox_valueChanged(int i); void checkerFinished(); private: diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 6ccffed4..19e23eba 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -44,8 +44,8 @@ Memory - - + + The maximum amount of memory Minecraft is allowed to use. @@ -67,27 +67,17 @@ - - + + - &Minimum memory allocation: + &PermGen: - minMemSpinBox + permGenSpinBox - - - - Ma&ximum memory allocation: - - - maxMemSpinBox - - - - + The amount of memory Minecraft is started with. @@ -109,17 +99,27 @@ - - + + - &PermGen: + Ma&ximum memory allocation: - permGenSpinBox + maxMemSpinBox - + + + + &Minimum memory allocation: + + + minMemSpinBox + + + + The amount of memory available to store loaded Java classes. @@ -141,6 +141,16 @@ + + + + + + + maxMemSpinBox + + + diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 5da7f19f..50039e87 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -59,12 +59,12 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent) { m_settings = inst->settings(); ui->setupUi(this); - auto sysMB = Sys::getSystemRam() / Sys::mebibyte; - ui->maxMemSpinBox->setMaximum(sysMB); + connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked); connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings); connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings); loadSettings(); + updateThresholds(); } bool InstanceSettingsPage::shouldDisplay() const @@ -437,6 +437,11 @@ void InstanceSettingsPage::on_javaTestBtn_clicked() checker->run(); } +void InstanceSettingsPage::on_maxMemSpinBox_valueChanged(int i) +{ + updateThresholds(); +} + void InstanceSettingsPage::checkerFinished() { checker.reset(); @@ -447,3 +452,20 @@ void InstanceSettingsPage::retranslate() ui->retranslateUi(this); ui->customCommands->retranslate(); // TODO: why is this seperate from the others? } + +void InstanceSettingsPage::updateThresholds() +{ + auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; + unsigned int maxMem = ui->maxMemSpinBox->value(); + + if (maxMem >= sysMiB) { + ui->labelMaxMemIcon->setText(u8"✘"); + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity.")); + } else if (maxMem > (sysMiB * 0.9)) { + ui->labelMaxMemIcon->setText(u8"⚠"); + ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); + } else { + ui->labelMaxMemIcon->setText(u8"✔"); + ui->labelMaxMemIcon->setToolTip(""); + } +} diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index 97d1296f..7450188d 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -77,10 +77,13 @@ public: virtual bool shouldDisplay() const override; void retranslate() override; + void updateThresholds(); + private slots: void on_javaDetectBtn_clicked(); void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); + void on_maxMemSpinBox_valueChanged(int i); void applySettings(); void loadSettings(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 8b3c3370..43488aa2 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -112,59 +112,29 @@ false - - - + + + - Minimum memory allocation: + PermGen: - - - - The maximum amount of memory Minecraft is allowed to use. - - - MiB - - - 128 - - - 65536 - - - 128 - - - 1024 + + + + Maximum memory allocation: - - - - The amount of memory Minecraft is started with. - - - MiB - - - 128 - - - 65536 - - - 128 - - - 256 + + + + Note: Permgen is set automatically by Java 8 and later - + The amount of memory available to store loaded Java classes. @@ -186,24 +156,67 @@ - - - - PermGen: + + + + The maximum amount of memory Minecraft is allowed to use. + + + MiB + + + 128 + + + 65536 + + + 128 + + + 1024 - - + + - Maximum memory allocation: + Minimum memory allocation: - - + + + + The amount of memory Minecraft is started with. + + + MiB + + + 128 + + + 65536 + + + 128 + + + 256 + + + + + - Note: Permgen is set automatically by Java 8 and later + + + + Qt::AlignCenter + + + maxMemSpinBox From 863a168cb5cb7332ae9782c3967d563f7f98316d Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 11 Nov 2022 15:04:18 +0100 Subject: [PATCH 043/277] feat: raise memory limits to 1TiB Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/global/JavaPage.ui | 4 ++-- launcher/ui/pages/instance/InstanceSettingsPage.ui | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 19e23eba..73461237 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -57,7 +57,7 @@ 128 - 65536 + 1048576 128 @@ -89,7 +89,7 @@ 128 - 65536 + 1048576 128 diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 43488aa2..2063dc55 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -168,7 +168,7 @@ 128 - 65536 + 1048576 128 @@ -197,7 +197,7 @@ 128 - 65536 + 1048576 128 From cabd887866929103f635a2640589ce95570f8573 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 11 Nov 2022 15:22:16 +0100 Subject: [PATCH 044/277] feat: use icons to show memory allocation state Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/global/JavaPage.cpp | 15 ++++++++++++--- .../ui/pages/instance/InstanceSettingsPage.cpp | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 00c06cff..81dd4cc1 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -196,14 +196,23 @@ void JavaPage::updateThresholds() auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; unsigned int maxMem = ui->maxMemSpinBox->value(); + QString iconName; + if (maxMem >= sysMiB) { - ui->labelMaxMemIcon->setText(u8"✘"); + iconName = "status-bad"; ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity.")); } else if (maxMem > (sysMiB * 0.9)) { - ui->labelMaxMemIcon->setText(u8"⚠"); + iconName = "status-yellow"; ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); } else { - ui->labelMaxMemIcon->setText(u8"✔"); + iconName = "status-good"; ui->labelMaxMemIcon->setToolTip(""); } + + { + auto height = ui->labelMaxMemIcon->fontInfo().pixelSize(); + QIcon icon = APPLICATION->getThemedIcon(iconName); + QPixmap pix = icon.pixmap(height, height); + ui->labelMaxMemIcon->setPixmap(pix); + } } diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 50039e87..af2ba7c8 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -458,14 +458,23 @@ void InstanceSettingsPage::updateThresholds() auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; unsigned int maxMem = ui->maxMemSpinBox->value(); + QString iconName; + if (maxMem >= sysMiB) { - ui->labelMaxMemIcon->setText(u8"✘"); + iconName = "status-bad"; ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity.")); } else if (maxMem > (sysMiB * 0.9)) { - ui->labelMaxMemIcon->setText(u8"⚠"); + iconName = "status-yellow"; ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); } else { - ui->labelMaxMemIcon->setText(u8"✔"); + iconName = "status-good"; ui->labelMaxMemIcon->setToolTip(""); } + + { + auto height = ui->labelMaxMemIcon->fontInfo().pixelSize(); + QIcon icon = APPLICATION->getThemedIcon(iconName); + QPixmap pix = icon.pixmap(height, height); + ui->labelMaxMemIcon->setPixmap(pix); + } } From b57fee1a444bef22f6a0f8f268dc1d11be31a0db Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 11 Nov 2022 15:24:14 +0100 Subject: [PATCH 045/277] fix: swap spin box and state icon Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/global/JavaPage.ui | 68 +++++------ .../ui/pages/instance/InstanceSettingsPage.ui | 106 +++++++++--------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 73461237..6749cbe4 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -44,26 +44,14 @@ Memory - - - - - The maximum amount of memory Minecraft is allowed to use. + + + + + Ma&ximum memory allocation: - - MiB - - - 128 - - - 1048576 - - - 128 - - - 1024 + + maxMemSpinBox @@ -77,6 +65,16 @@ + + + + &Minimum memory allocation: + + + minMemSpinBox + + + @@ -99,23 +97,25 @@ - - - - Ma&ximum memory allocation: + + + + The maximum amount of memory Minecraft is allowed to use. - - maxMemSpinBox + + MiB - - - - - - &Minimum memory allocation: + + 128 - - minMemSpinBox + + 1048576 + + + 128 + + + 1024 @@ -141,7 +141,7 @@ - + diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 2063dc55..b064367d 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -112,7 +112,7 @@ false - + @@ -120,6 +120,13 @@ + + + + Minimum memory allocation: + + + @@ -134,57 +141,6 @@ - - - - The amount of memory available to store loaded Java classes. - - - MiB - - - 64 - - - 999999999 - - - 8 - - - 64 - - - - - - - The maximum amount of memory Minecraft is allowed to use. - - - MiB - - - 128 - - - 1048576 - - - 128 - - - 1024 - - - - - - - Minimum memory allocation: - - - @@ -207,7 +163,51 @@ - + + + + The maximum amount of memory Minecraft is allowed to use. + + + MiB + + + 128 + + + 1048576 + + + 128 + + + 1024 + + + + + + + The amount of memory available to store loaded Java classes. + + + MiB + + + 64 + + + 999999999 + + + 8 + + + 64 + + + + From 5909af9878d67f2900c92e8d1dd3eec7a53a840a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 11 Nov 2022 14:10:45 -0700 Subject: [PATCH 046/277] drag&drop + add folder to watch Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .../flame/FlameInstanceCreationTask.cpp | 8 +- .../modpacksch/FTBPackInstallTask.cpp | 6 +- launcher/ui/dialogs/BlockedModsDialog.cpp | 82 +++++++++++++++++-- launcher/ui/dialogs/BlockedModsDialog.h | 8 ++ 4 files changed, 92 insertions(+), 12 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index f0fbdc96..eff729eb 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -393,8 +393,12 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) qWarning() << "Blocked mods found, displaying mod list"; auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked mods found"), - tr("The following mods were blocked on third party launchers.
" - "You will need to manually download them and add them to the modpack"), + tr("The following files are not available for download in third party launchers.
" + "You will need to manually download them and add them to the instance.

" + "Your configured global mods folder and default downloads folder
" + "are automatically checked for the downloaded mods.
" + "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch
" + "if you did not download the mods to a default location."), blocked_mods); message_dialog->setModal(true); diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp index 40aee82b..d74faf08 100644 --- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp +++ b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp @@ -212,7 +212,11 @@ void PackInstallTask::onResolveModsSucceeded() auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked files found"), tr("The following files are not available for download in third party launchers.
" - "You will need to manually download them and add them to the instance."), + "You will need to manually download them and add them to the instance.

" + "Your configured global mods folder and default downloads folder
" + "are automatically checked for the downloaded mods.
" + "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch
" + "if you did not download the mods to a default location."), m_blocked_mods); if (message_dialog->exec() == QDialog::Accepted) { diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index 2cf94250..ca757cbc 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -5,20 +5,29 @@ #include "Application.h" #include "ui_BlockedModsDialog.h" + #include #include +#include +#include +#include BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods) : QDialog(parent), ui(new Ui::BlockedModsDialog), mods(mods) { + hashing_task = shared_qobject_ptr(new ConcurrentTask(this, "MakeHashesTask", 10)); + ui->setupUi(this); auto openAllButton = ui->buttonBox->addButton(tr("Open All"), QDialogButtonBox::ActionRole); connect(openAllButton, &QPushButton::clicked, this, &BlockedModsDialog::openAll); + auto downloadFolderButton = ui->buttonBox->addButton(tr("Add Download Folder"), QDialogButtonBox::ActionRole); + connect(downloadFolderButton, &QPushButton::clicked, this, &BlockedModsDialog::addDownloadFolder); + connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &BlockedModsDialog::directoryChanged); - hashing_task = shared_qobject_ptr(new ConcurrentTask(this, "MakeHashesTask", 10)); + qDebug() << "Mods List: " << mods; @@ -28,6 +37,9 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons this->setWindowTitle(title); ui->label->setText(text); ui->labelModsFound->setText(tr("Please download the missing mods.")); + + setAcceptDrops(true); + update(); } @@ -36,6 +48,22 @@ BlockedModsDialog::~BlockedModsDialog() delete ui; } +void BlockedModsDialog::dragEnterEvent(QDragEnterEvent *e) { + if (e->mimeData()->hasUrls()) { + e->acceptProposedAction(); + } +} + +void BlockedModsDialog::dropEvent(QDropEvent *e) +{ + foreach (const QUrl &url, e->mimeData()->urls()) { + QString file = url.toLocalFile(); + qDebug() << "Dropped file:" << file; + addHashTask(file); + } + hashing_task->start(); +} + void BlockedModsDialog::openAll() { for (auto& mod : mods) { @@ -43,6 +71,16 @@ void BlockedModsDialog::openAll() } } +void BlockedModsDialog::addDownloadFolder() { + QString dir = QFileDialog::getExistingDirectory( + this, + tr("Select directory where you downloaded the mods"), + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation), + QFileDialog::ShowDirsOnly); + watcher.addPath(dir); + scanPath(dir); +} + /// @brief update UI with current status of the blocked mod detection void BlockedModsDialog::update() { @@ -74,6 +112,7 @@ void BlockedModsDialog::update() void BlockedModsDialog::directoryChanged(QString path) { qDebug() << "Directory changed: " << path; + validateMatchedMods(); scanPath(path); } @@ -108,19 +147,26 @@ void BlockedModsDialog::scanPath(QString path) continue; } - auto hash_task = Hashing::createBlockedModHasher(file, ModPlatform::Provider::FLAME, "sha1"); - - qDebug() << "Creating Hash task for path: " << file; - - connect(hash_task.get(), &Task::succeeded, [this, hash_task, file] { checkMatchHash(hash_task->getResult(), file); }); - connect(hash_task.get(), &Task::failed, [file] { qDebug() << "Failed to hash path: " << file; }); - - hashing_task->addTask(hash_task); + addHashTask(file); } hashing_task->start(); } +/// @brief add a hashing task for the file located at path and connect it to check that hash against +/// our blocked mods list +/// @param path the path to the local file being hashed +void BlockedModsDialog::addHashTask(QString path) { + auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::Provider::FLAME, "sha1"); + + qDebug() << "Creating Hash task for path: " << path; + + connect(hash_task.get(), &Task::succeeded, [this, hash_task, path] { checkMatchHash(hash_task->getResult(), path); }); + connect(hash_task.get(), &Task::failed, [path] { qDebug() << "Failed to hash path: " << path; }); + + hashing_task->addTask(hash_task); +} + /// @brief check if the computed hash for the provided path matches a blocked /// mod we are looking for /// @param hash the computed hash for the provided path @@ -174,6 +220,24 @@ bool BlockedModsDialog::allModsMatched() return std::all_of(mods.begin(), mods.end(), [](auto const& mod) { return mod.matched; }); } +/// @brief ensure matched file paths still exist +void BlockedModsDialog::validateMatchedMods() { + bool changed = false; + for (auto& mod : mods) { + if (mod.matched) { + QFileInfo file = QFileInfo(mod.localPath); + if (!file.exists() || !file.isFile()) { + mod.localPath = ""; + mod.matched = false; + changed = true; + } + } + } + if (changed) { + update(); + } +} + /// qDebug print support for the BlockedMod struct QDebug operator<<(QDebug debug, const BlockedMod& m) { diff --git a/launcher/ui/dialogs/BlockedModsDialog.h b/launcher/ui/dialogs/BlockedModsDialog.h index 0a5c90db..d683c54d 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.h +++ b/launcher/ui/dialogs/BlockedModsDialog.h @@ -31,6 +31,11 @@ public: ~BlockedModsDialog() override; +protected: + void dragEnterEvent(QDragEnterEvent *event) override; + // void dragMoveEvent(QDragMoveEvent *event) override; + // void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent *event) override; private: Ui::BlockedModsDialog *ui; @@ -39,12 +44,15 @@ private: shared_qobject_ptr hashing_task; void openAll(); + void addDownloadFolder(); void update(); void directoryChanged(QString path); void setupWatch(); void scanPaths(); void scanPath(QString path); + void addHashTask(QString path); void checkMatchHash(QString hash, QString path); + void validateMatchedMods(); bool checkValidPath(QString path); bool allModsMatched(); From 3f6cc178188a28e551755219bfa0a441ad8cfdcf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 11 Nov 2022 15:45:40 -0700 Subject: [PATCH 047/277] properly handle a currently running hashing task Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .../flame/FlameInstanceCreationTask.cpp | 2 +- .../modpacksch/FTBPackInstallTask.cpp | 2 +- launcher/ui/dialogs/BlockedModsDialog.cpp | 113 +++++++++++++----- launcher/ui/dialogs/BlockedModsDialog.h | 13 +- 4 files changed, 94 insertions(+), 36 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index eff729eb..4b9e7b86 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -396,7 +396,7 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) tr("The following files are not available for download in third party launchers.
" "You will need to manually download them and add them to the instance.

" "Your configured global mods folder and default downloads folder
" - "are automatically checked for the downloaded mods.
" + "are automatically checked for the downloaded mods and they will be copied to the instance if found.
" "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch
" "if you did not download the mods to a default location."), blocked_mods); diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp index d74faf08..26d0c5a1 100644 --- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp +++ b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp @@ -214,7 +214,7 @@ void PackInstallTask::onResolveModsSucceeded() tr("The following files are not available for download in third party launchers.
" "You will need to manually download them and add them to the instance.

" "Your configured global mods folder and default downloads folder
" - "are automatically checked for the downloaded mods.
" + "are automatically checked for the downloaded mods and they will be copied to the instance if found.
" "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch
" "if you did not download the mods to a default location."), m_blocked_mods); diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index ca757cbc..a1941cb8 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -1,4 +1,5 @@ #include "BlockedModsDialog.h" +#include #include #include #include @@ -13,9 +14,10 @@ #include BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods) - : QDialog(parent), ui(new Ui::BlockedModsDialog), mods(mods) + : QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods) { - hashing_task = shared_qobject_ptr(new ConcurrentTask(this, "MakeHashesTask", 10)); + m_hashing_task = shared_qobject_ptr(new ConcurrentTask(this, "MakeHashesTask", 10)); + connect(m_hashing_task.get(), &Task::finished, this, &BlockedModsDialog::hashTaskFinished); ui->setupUi(this); @@ -25,11 +27,11 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons auto downloadFolderButton = ui->buttonBox->addButton(tr("Add Download Folder"), QDialogButtonBox::ActionRole); connect(downloadFolderButton, &QPushButton::clicked, this, &BlockedModsDialog::addDownloadFolder); - connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &BlockedModsDialog::directoryChanged); + connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &BlockedModsDialog::directoryChanged); - qDebug() << "Mods List: " << mods; + qDebug() << "[Blocked Mods Dialog] Mods List: " << mods; setupWatch(); scanPaths(); @@ -57,16 +59,23 @@ void BlockedModsDialog::dragEnterEvent(QDragEnterEvent *e) { void BlockedModsDialog::dropEvent(QDropEvent *e) { foreach (const QUrl &url, e->mimeData()->urls()) { - QString file = url.toLocalFile(); - qDebug() << "Dropped file:" << file; - addHashTask(file); + QString filePath = url.toLocalFile(); + qDebug() << "[Blocked Mods Dialog] Dropped file:" << filePath; + addHashTask(filePath); + + // watch for changes + QFileInfo file = QFileInfo(filePath); + QString path = file.dir().absolutePath(); + qDebug() << "[Blocked Mods Dialog] Adding watch path:" << path; + m_watcher.addPath(path); } - hashing_task->start(); + scanPaths(); + update(); } void BlockedModsDialog::openAll() { - for (auto& mod : mods) { + for (auto& mod : m_mods) { QDesktopServices::openUrl(mod.websiteUrl); } } @@ -77,8 +86,10 @@ void BlockedModsDialog::addDownloadFolder() { tr("Select directory where you downloaded the mods"), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation), QFileDialog::ShowDirsOnly); - watcher.addPath(dir); - scanPath(dir); + qDebug() << "[Blocked Mods Dialog] Adding watch path:" << dir; + m_watcher.addPath(dir); + scanPath(dir, true); + update(); } /// @brief update UI with current status of the blocked mod detection @@ -87,7 +98,7 @@ void BlockedModsDialog::update() QString text; QString span; - for (auto& mod : mods) { + for (auto& mod : m_mods) { if (mod.matched) { // ✔ -> html for HEAVY CHECK MARK : ✔ span = QString(tr(" ✔ Found at %1 ")).arg(mod.localPath); @@ -111,9 +122,9 @@ void BlockedModsDialog::update() /// @param path the path to the changed directory void BlockedModsDialog::directoryChanged(QString path) { - qDebug() << "Directory changed: " << path; + qDebug() << "[Blocked Mods Dialog] Directory changed: " << path; validateMatchedMods(); - scanPath(path); + scanPath(path, true); } /// @brief add the user downloads folder and the global mods folder to the filesystem watcher @@ -121,22 +132,23 @@ void BlockedModsDialog::setupWatch() { const QString downloadsFolder = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); const QString modsFolder = APPLICATION->settings()->get("CentralModsDir").toString(); - watcher.addPath(downloadsFolder); - watcher.addPath(modsFolder); + m_watcher.addPath(downloadsFolder); + m_watcher.addPath(modsFolder); } /// @brief scan all watched folder void BlockedModsDialog::scanPaths() { - for (auto& dir : watcher.directories()) { - scanPath(dir); + for (auto& dir : m_watcher.directories()) { + scanPath(dir, false); } + runHashTask(); } /// @brief Scan the directory at path, skip paths that do not contain a file name /// of a blocked mod we are looking for /// @param path the directory to scan -void BlockedModsDialog::scanPath(QString path) +void BlockedModsDialog::scanPath(QString path, bool start_task) { QDir scan_dir(path); QDirIterator scan_it(path, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::NoIteratorFlags); @@ -150,21 +162,35 @@ void BlockedModsDialog::scanPath(QString path) addHashTask(file); } - hashing_task->start(); + if (start_task) { + runHashTask(); + } + +} + +/// @brief add a hashing task for the file located at path, add the path to the pending set if the hasing task is already running +/// @param path the path to the local file being hashed +void BlockedModsDialog::addHashTask(QString path) { + if (m_hashing_task->isRunning()) { + qDebug() << "[Blocked Mods Dialog] adding a Hash task for" << path << "to the pending set."; + m_pending_hash_paths.insert(path); + } else { + buildHashTask(path); + } } /// @brief add a hashing task for the file located at path and connect it to check that hash against /// our blocked mods list /// @param path the path to the local file being hashed -void BlockedModsDialog::addHashTask(QString path) { +void BlockedModsDialog::buildHashTask(QString path) { auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::Provider::FLAME, "sha1"); - qDebug() << "Creating Hash task for path: " << path; + qDebug() << "[Blocked Mods Dialog] Creating Hash task for path: " << path; connect(hash_task.get(), &Task::succeeded, [this, hash_task, path] { checkMatchHash(hash_task->getResult(), path); }); connect(hash_task.get(), &Task::failed, [path] { qDebug() << "Failed to hash path: " << path; }); - hashing_task->addTask(hash_task); + m_hashing_task->addTask(hash_task); } /// @brief check if the computed hash for the provided path matches a blocked @@ -175,9 +201,9 @@ void BlockedModsDialog::checkMatchHash(QString hash, QString path) { bool match = false; - qDebug() << "Checking for match on hash: " << hash << "| From path:" << path; + qDebug() << "[Blocked Mods Dialog] Checking for match on hash: " << hash << "| From path:" << path; - for (auto& mod : mods) { + for (auto& mod : m_mods) { if (mod.matched) { continue; } @@ -186,7 +212,7 @@ void BlockedModsDialog::checkMatchHash(QString hash, QString path) mod.localPath = path; match = true; - qDebug() << "Hash match found:" << mod.name << hash << "| From path:" << path; + qDebug() << "[Blocked Mods Dialog] Hash match found:" << mod.name << hash << "| From path:" << path; break; } @@ -205,9 +231,9 @@ bool BlockedModsDialog::checkValidPath(QString path) QFileInfo file = QFileInfo(path); QString filename = file.fileName(); - for (auto& mod : mods) { + for (auto& mod : m_mods) { if (mod.name.compare(filename, Qt::CaseInsensitive) == 0) { - qDebug() << "Name match found:" << mod.name << "| From path:" << path; + qDebug() << "[Blocked Mods Dialog] Name match found:" << mod.name << "| From path:" << path; return true; } } @@ -217,13 +243,13 @@ bool BlockedModsDialog::checkValidPath(QString path) bool BlockedModsDialog::allModsMatched() { - return std::all_of(mods.begin(), mods.end(), [](auto const& mod) { return mod.matched; }); + return std::all_of(m_mods.begin(), m_mods.end(), [](auto const& mod) { return mod.matched; }); } /// @brief ensure matched file paths still exist void BlockedModsDialog::validateMatchedMods() { bool changed = false; - for (auto& mod : mods) { + for (auto& mod : m_mods) { if (mod.matched) { QFileInfo file = QFileInfo(mod.localPath); if (!file.exists() || !file.isFile()) { @@ -238,6 +264,33 @@ void BlockedModsDialog::validateMatchedMods() { } } +/// @brief run hast task or mark a pending run if it is already runing +void BlockedModsDialog::runHashTask() { + if (!m_hashing_task->isRunning()) { + m_rehash_pending = false; + m_hashing_task->start(); + } else { + qDebug() << "[Blocked Mods Dialog] queueing another run of the hashing task"; + qDebug() << "[Blocked Mods Dialog] pending hash tasks:" << m_pending_hash_paths; + m_rehash_pending = true; + } +} + +void BlockedModsDialog::hashTaskFinished() { + qDebug() << "[Blocked Mods Dialog] All hash tasks finished"; + if (m_rehash_pending) { + qDebug() << "[Blocked Mods Dialog] there was a pending rehash, building and running tasks"; + + auto path = m_pending_hash_paths.begin(); + while (path != m_pending_hash_paths.end()) { + buildHashTask(*path); + path = m_pending_hash_paths.erase(path); + } + + runHashTask(); + } +} + /// qDebug print support for the BlockedMod struct QDebug operator<<(QDebug debug, const BlockedMod& m) { diff --git a/launcher/ui/dialogs/BlockedModsDialog.h b/launcher/ui/dialogs/BlockedModsDialog.h index d683c54d..ee1e6a09 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.h +++ b/launcher/ui/dialogs/BlockedModsDialog.h @@ -39,9 +39,11 @@ protected: private: Ui::BlockedModsDialog *ui; - QList &mods; - QFileSystemWatcher watcher; - shared_qobject_ptr hashing_task; + QList &m_mods; + QFileSystemWatcher m_watcher; + shared_qobject_ptr m_hashing_task; + QSet m_pending_hash_paths; + bool m_rehash_pending; void openAll(); void addDownloadFolder(); @@ -49,10 +51,13 @@ private: void directoryChanged(QString path); void setupWatch(); void scanPaths(); - void scanPath(QString path); + void scanPath(QString path, bool start_task); void addHashTask(QString path); + void buildHashTask(QString path); void checkMatchHash(QString hash, QString path); void validateMatchedMods(); + void runHashTask(); + void hashTaskFinished(); bool checkValidPath(QString path); bool allModsMatched(); From b9e2c3524ce39769396d1b4d6a2bf1b50a9f0996 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 11 Nov 2022 16:07:57 -0700 Subject: [PATCH 048/277] chore: clang format & cleanup Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/dialogs/BlockedModsDialog.cpp | 45 ++++++++++++----------- launcher/ui/dialogs/BlockedModsDialog.h | 2 - 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index a1941cb8..04d37963 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -6,19 +6,18 @@ #include "Application.h" #include "ui_BlockedModsDialog.h" - #include -#include #include #include #include +#include BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods) : QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods) { m_hashing_task = shared_qobject_ptr(new ConcurrentTask(this, "MakeHashesTask", 10)); connect(m_hashing_task.get(), &Task::finished, this, &BlockedModsDialog::hashTaskFinished); - + ui->setupUi(this); auto openAllButton = ui->buttonBox->addButton(tr("Open All"), QDialogButtonBox::ActionRole); @@ -29,8 +28,6 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &BlockedModsDialog::directoryChanged); - - qDebug() << "[Blocked Mods Dialog] Mods List: " << mods; setupWatch(); @@ -50,15 +47,16 @@ BlockedModsDialog::~BlockedModsDialog() delete ui; } -void BlockedModsDialog::dragEnterEvent(QDragEnterEvent *e) { +void BlockedModsDialog::dragEnterEvent(QDragEnterEvent* e) +{ if (e->mimeData()->hasUrls()) { e->acceptProposedAction(); } } -void BlockedModsDialog::dropEvent(QDropEvent *e) +void BlockedModsDialog::dropEvent(QDropEvent* e) { - foreach (const QUrl &url, e->mimeData()->urls()) { + foreach (const QUrl& url, e->mimeData()->urls()) { QString filePath = url.toLocalFile(); qDebug() << "[Blocked Mods Dialog] Dropped file:" << filePath; addHashTask(filePath); @@ -80,12 +78,11 @@ void BlockedModsDialog::openAll() } } -void BlockedModsDialog::addDownloadFolder() { - QString dir = QFileDialog::getExistingDirectory( - this, - tr("Select directory where you downloaded the mods"), - QStandardPaths::writableLocation(QStandardPaths::DownloadLocation), - QFileDialog::ShowDirsOnly); +void BlockedModsDialog::addDownloadFolder() +{ + QString dir = + QFileDialog::getExistingDirectory(this, tr("Select directory where you downloaded the mods"), + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation), QFileDialog::ShowDirsOnly); qDebug() << "[Blocked Mods Dialog] Adding watch path:" << dir; m_watcher.addPath(dir); scanPath(dir, true); @@ -165,12 +162,12 @@ void BlockedModsDialog::scanPath(QString path, bool start_task) if (start_task) { runHashTask(); } - } /// @brief add a hashing task for the file located at path, add the path to the pending set if the hasing task is already running /// @param path the path to the local file being hashed -void BlockedModsDialog::addHashTask(QString path) { +void BlockedModsDialog::addHashTask(QString path) +{ if (m_hashing_task->isRunning()) { qDebug() << "[Blocked Mods Dialog] adding a Hash task for" << path << "to the pending set."; m_pending_hash_paths.insert(path); @@ -179,10 +176,11 @@ void BlockedModsDialog::addHashTask(QString path) { } } -/// @brief add a hashing task for the file located at path and connect it to check that hash against +/// @brief add a hashing task for the file located at path and connect it to check that hash against /// our blocked mods list /// @param path the path to the local file being hashed -void BlockedModsDialog::buildHashTask(QString path) { +void BlockedModsDialog::buildHashTask(QString path) +{ auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::Provider::FLAME, "sha1"); qDebug() << "[Blocked Mods Dialog] Creating Hash task for path: " << path; @@ -247,7 +245,8 @@ bool BlockedModsDialog::allModsMatched() } /// @brief ensure matched file paths still exist -void BlockedModsDialog::validateMatchedMods() { +void BlockedModsDialog::validateMatchedMods() +{ bool changed = false; for (auto& mod : m_mods) { if (mod.matched) { @@ -264,8 +263,9 @@ void BlockedModsDialog::validateMatchedMods() { } } -/// @brief run hast task or mark a pending run if it is already runing -void BlockedModsDialog::runHashTask() { +/// @brief run hash task or mark a pending run if it is already runing +void BlockedModsDialog::runHashTask() +{ if (!m_hashing_task->isRunning()) { m_rehash_pending = false; m_hashing_task->start(); @@ -276,7 +276,8 @@ void BlockedModsDialog::runHashTask() { } } -void BlockedModsDialog::hashTaskFinished() { +void BlockedModsDialog::hashTaskFinished() +{ qDebug() << "[Blocked Mods Dialog] All hash tasks finished"; if (m_rehash_pending) { qDebug() << "[Blocked Mods Dialog] there was a pending rehash, building and running tasks"; diff --git a/launcher/ui/dialogs/BlockedModsDialog.h b/launcher/ui/dialogs/BlockedModsDialog.h index ee1e6a09..dac43cba 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.h +++ b/launcher/ui/dialogs/BlockedModsDialog.h @@ -33,8 +33,6 @@ public: protected: void dragEnterEvent(QDragEnterEvent *event) override; - // void dragMoveEvent(QDragMoveEvent *event) override; - // void dragLeaveEvent(QDragLeaveEvent *event) override; void dropEvent(QDropEvent *event) override; private: From 51c27e2748cca3d772cdd9656325b748a819f3c9 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 11 Nov 2022 16:33:52 -0700 Subject: [PATCH 049/277] always use the pending path set to avoid task duplication. Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/dialogs/BlockedModsDialog.cpp | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index 04d37963..ff331617 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -168,12 +168,8 @@ void BlockedModsDialog::scanPath(QString path, bool start_task) /// @param path the path to the local file being hashed void BlockedModsDialog::addHashTask(QString path) { - if (m_hashing_task->isRunning()) { - qDebug() << "[Blocked Mods Dialog] adding a Hash task for" << path << "to the pending set."; - m_pending_hash_paths.insert(path); - } else { - buildHashTask(path); - } + qDebug() << "[Blocked Mods Dialog] adding a Hash task for" << path << "to the pending set."; + m_pending_hash_paths.insert(path); } /// @brief add a hashing task for the file located at path and connect it to check that hash against @@ -252,6 +248,8 @@ void BlockedModsDialog::validateMatchedMods() if (mod.matched) { QFileInfo file = QFileInfo(mod.localPath); if (!file.exists() || !file.isFile()) { + qDebug() << "[Blocked Mods Dialog] File" << mod.localPath << "for mod" << mod.name + << "has vanshed! marking as not matched."; mod.localPath = ""; mod.matched = false; changed = true; @@ -268,7 +266,18 @@ void BlockedModsDialog::runHashTask() { if (!m_hashing_task->isRunning()) { m_rehash_pending = false; - m_hashing_task->start(); + + if (!m_pending_hash_paths.isEmpty()) { + qDebug() << "[Blocked Mods Dialog] there are pending hash tasks, building and running tasks"; + + auto path = m_pending_hash_paths.begin(); + while (path != m_pending_hash_paths.end()) { + buildHashTask(*path); + path = m_pending_hash_paths.erase(path); + } + + m_hashing_task->start(); + } } else { qDebug() << "[Blocked Mods Dialog] queueing another run of the hashing task"; qDebug() << "[Blocked Mods Dialog] pending hash tasks:" << m_pending_hash_paths; @@ -280,14 +289,7 @@ void BlockedModsDialog::hashTaskFinished() { qDebug() << "[Blocked Mods Dialog] All hash tasks finished"; if (m_rehash_pending) { - qDebug() << "[Blocked Mods Dialog] there was a pending rehash, building and running tasks"; - - auto path = m_pending_hash_paths.begin(); - while (path != m_pending_hash_paths.end()) { - buildHashTask(*path); - path = m_pending_hash_paths.erase(path); - } - + qDebug() << "[Blocked Mods Dialog] task finished with a rehash pending, rerunning"; runHashTask(); } } From 9af1b00df52d974a53925345efcd5c047b7d041c Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 11 Nov 2022 18:05:19 -0700 Subject: [PATCH 050/277] feat: add list of watched folders move explanation text into dialog class and it's own label Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .../flame/FlameInstanceCreationTask.cpp | 11 ++-- .../modpacksch/FTBPackInstallTask.cpp | 15 ++--- launcher/ui/dialogs/BlockedModsDialog.cpp | 27 +++++++-- launcher/ui/dialogs/BlockedModsDialog.ui | 58 ++++++++++++++++++- 4 files changed, 86 insertions(+), 25 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 4b9e7b86..91554b58 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -393,13 +393,10 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) qWarning() << "Blocked mods found, displaying mod list"; auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked mods found"), - tr("The following files are not available for download in third party launchers.
" - "You will need to manually download them and add them to the instance.

" - "Your configured global mods folder and default downloads folder
" - "are automatically checked for the downloaded mods and they will be copied to the instance if found.
" - "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch
" - "if you did not download the mods to a default location."), - blocked_mods); + tr("The following files are not available for download in third party launchers.
" + "You will need to manually download them and add them to the instance."), + blocked_mods); + message_dialog->setModal(true); if (message_dialog->exec()) { diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp index 26d0c5a1..4c7b7a4f 100644 --- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp +++ b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp @@ -211,22 +211,17 @@ void PackInstallTask::onResolveModsSucceeded() qDebug() << "Blocked files found, displaying file list"; auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked files found"), - tr("The following files are not available for download in third party launchers.
" - "You will need to manually download them and add them to the instance.

" - "Your configured global mods folder and default downloads folder
" - "are automatically checked for the downloaded mods and they will be copied to the instance if found.
" - "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch
" - "if you did not download the mods to a default location."), - m_blocked_mods); + tr("The following files are not available for download in third party launchers.
" + "You will need to manually download them and add them to the instance."), + m_blocked_mods); if (message_dialog->exec() == QDialog::Accepted) { qDebug() << "Post dialog blocked mods list: " << m_blocked_mods; createInstance(); - } - else { + } else { abort(); } - + } else { createInstance(); } diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index ff331617..7f6b377b 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -1,5 +1,4 @@ #include "BlockedModsDialog.h" -#include #include #include #include @@ -34,7 +33,16 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons scanPaths(); this->setWindowTitle(title); - ui->label->setText(text); + ui->labelDescription->setText(text); + ui->labelExplain->setText( + QString(tr("Your configured global mods folder and default downloads folder " + "are automatically checked for the downloaded mods and they will be copied to the instance if found.
" + "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch " + "if you did not download the mods to a default location.

" + "Global Mods Folder: %1
" + "Default Downloads Folder: %2")) + .arg(APPLICATION->settings()->get("CentralModsDir").toString(), + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))); ui->labelModsFound->setText(tr("Please download the missing mods.")); setAcceptDrops(true); @@ -56,7 +64,7 @@ void BlockedModsDialog::dragEnterEvent(QDragEnterEvent* e) void BlockedModsDialog::dropEvent(QDropEvent* e) { - foreach (const QUrl& url, e->mimeData()->urls()) { + for (const QUrl& url : e->mimeData()->urls()) { QString filePath = url.toLocalFile(); qDebug() << "[Blocked Mods Dialog] Dropped file:" << filePath; addHashTask(filePath); @@ -106,7 +114,14 @@ void BlockedModsDialog::update() text += QString(tr("%1: %2

Hash: %3 %4


")).arg(mod.name, mod.websiteUrl, mod.hash, span); } - ui->textBrowser->setText(text); + ui->textBrowserModsListing->setText(text); + + QString watching; + for (auto& dir : m_watcher.directories()) { + watching += QString("%1
").arg(dir); + } + + ui->textBrowserWatched->setText(watching); if (allModsMatched()) { ui->labelModsFound->setText(tr("All mods found ✔")); @@ -181,8 +196,8 @@ void BlockedModsDialog::buildHashTask(QString path) qDebug() << "[Blocked Mods Dialog] Creating Hash task for path: " << path; - connect(hash_task.get(), &Task::succeeded, [this, hash_task, path] { checkMatchHash(hash_task->getResult(), path); }); - connect(hash_task.get(), &Task::failed, [path] { qDebug() << "Failed to hash path: " << path; }); + connect(hash_task.get(), &Task::succeeded, this, [this, hash_task, path] { checkMatchHash(hash_task->getResult(), path); }); + connect(hash_task.get(), &Task::failed, this, [path] { qDebug() << "Failed to hash path: " << path; }); m_hashing_task->addTask(hash_task); } diff --git a/launcher/ui/dialogs/BlockedModsDialog.ui b/launcher/ui/dialogs/BlockedModsDialog.ui index 371549cf..fb1036b7 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.ui +++ b/launcher/ui/dialogs/BlockedModsDialog.ui @@ -15,17 +15,36 @@ - + Qt::RichText + + true + - + + + + + + true + + + + + + + + 0 + 165 + + true @@ -34,6 +53,41 @@ + + + + + 0 + 1 + + + + Watched Folders: + + + + + + + + 0 + 0 + + + + + 0 + 16 + + + + + 0 + 12 + + + + From 25b306b7e1c83ecdeb135c8100c3089d9147cde7 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 11 Nov 2022 18:32:11 -0700 Subject: [PATCH 051/277] feat: mark folder paths as links Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/dialogs/BlockedModsDialog.cpp | 9 ++++++--- launcher/ui/dialogs/BlockedModsDialog.ui | 9 +++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index 7f6b377b..a0dd1a10 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -39,12 +39,15 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons "are automatically checked for the downloaded mods and they will be copied to the instance if found.
" "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch " "if you did not download the mods to a default location.

" - "Global Mods Folder: %1
" - "Default Downloads Folder: %2")) + "Global Mods Folder: %1
" + "Default Downloads Folder: %2")) .arg(APPLICATION->settings()->get("CentralModsDir").toString(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))); ui->labelModsFound->setText(tr("Please download the missing mods.")); + // force all URL handeling as external + connect(ui->textBrowserWatched, &QTextBrowser::anchorClicked, this, [](const QUrl url) { QDesktopServices::openUrl(url); }); + setAcceptDrops(true); update(); @@ -118,7 +121,7 @@ void BlockedModsDialog::update() QString watching; for (auto& dir : m_watcher.directories()) { - watching += QString("%1
").arg(dir); + watching += QString("%1
").arg(dir); } ui->textBrowserWatched->setText(watching); diff --git a/launcher/ui/dialogs/BlockedModsDialog.ui b/launcher/ui/dialogs/BlockedModsDialog.ui index fb1036b7..88105178 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.ui +++ b/launcher/ui/dialogs/BlockedModsDialog.ui @@ -35,6 +35,9 @@ true + + true +
@@ -86,6 +89,12 @@ 12 + + true + + + false + From 30b266622c9457a825d38ba084c9391437a2c87a Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 22 Oct 2022 15:39:53 +0300 Subject: [PATCH 052/277] Add 'Create shortcut' button to instance toolbar Implemented on Windows only rn! Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 86 ++++++++++++++++++++++++++++++++++++++ launcher/ui/MainWindow.h | 2 + 2 files changed, 88 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 85b00b67..419bb9bd 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -237,6 +237,7 @@ public: TranslatedAction actionLaunchInstanceOffline; TranslatedAction actionLaunchInstanceDemo; TranslatedAction actionExportInstance; + TranslatedAction actionCreateInstanceShortcut; QVector all_actions; LabeledToolButton *renameButton = nullptr; @@ -597,6 +598,7 @@ public: actionExportInstance->setEnabled(enabled); actionDeleteInstance->setEnabled(enabled); actionCopyInstance->setEnabled(enabled); + actionCreateInstanceShortcut->setEnabled(enabled); } void createStatusBar(QMainWindow *MainWindow) @@ -735,6 +737,14 @@ public: actionCopyInstance->setIcon(APPLICATION->getThemedIcon("copy")); all_actions.append(&actionCopyInstance); + actionCreateInstanceShortcut = TranslatedAction(MainWindow); + actionCreateInstanceShortcut->setObjectName(QStringLiteral("actionCreateInstanceShortcut")); + actionCreateInstanceShortcut.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Create shortcut")); + actionCreateInstanceShortcut.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Creates a shortcut on your desktop to launch the selected instance.")); + actionCreateInstanceShortcut->setShortcut(QKeySequence(tr("Ctrl+D"))); + //actionCreateInstanceShortcut->setIcon(APPLICATION->getThemedIcon("copy")); // TODO + all_actions.append(&actionCreateInstanceShortcut); + setInstanceActionsEnabled(false); } @@ -783,6 +793,8 @@ public: } } + instanceToolBar->addAction(actionCreateInstanceShortcut); + all_toolbars.append(&instanceToolBar); MainWindow->addToolBar(Qt::RightToolBarArea, instanceToolBar); } @@ -2075,6 +2087,80 @@ void MainWindow::on_actionKillInstance_triggered() } } +#ifdef Q_OS_WIN +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#include "winnls.h" +#include "shobjidl.h" +#include "objbase.h" +#include "objidl.h" +#include "shlguid.h" +#endif + +void MainWindow::on_actionCreateInstanceShortcut_triggered() +{ + if (m_selectedInstance) + { + auto desktopDir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + if (desktopDir.isEmpty()) { + // TODO come up with an alternative solution (open "save file" dialog) + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop!")); + return; + } + +#if defined(Q_OS_WIN) + // Windows + WCHAR wsz[MAX_PATH]; + + // ...yes, you need to initialize the entire COM stack to make a shortcut in Windows. + // I hate it. + CoInitialize(nullptr); + + HRESULT hres; + IShellLink* psl; + + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); + if (SUCCEEDED(hres)) + { + IPersistFile* ppf; + + QApplication::applicationFilePath().left(MAX_PATH - 1).toWCharArray(wsz); + psl->SetPath(wsz); + + wmemset(wsz, 0, MAX_PATH); + QStringLiteral("--launch %1").arg(m_selectedInstance->id()).left(MAX_PATH - 1).toWCharArray(wsz); + psl->SetArguments(wsz); + + // TODO set icon + + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); + + if (SUCCEEDED(hres)) + { + wmemset(wsz, 0, MAX_PATH); + + desktopDir + .append('/') + .append(QStringLiteral("%1.lnk").arg(m_selectedInstance->name())) + .left(MAX_PATH - 1).toWCharArray(wsz); + + hres = ppf->Save(wsz, TRUE); + ppf->Release(); + } + psl->Release(); + } + + CoUninitialize(); +#elif defined(Q_OS_LINUX) + // Linux +#elif defined(Q_OS_MACOS) + // macOSX + // TODO actually write this path + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOSX yet!")); +#endif + } +} + void MainWindow::taskEnd() { QObject *sender = QObject::sender(); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index f9d1f1c7..b23ba146 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -157,6 +157,8 @@ private slots: void on_actionEditInstance_triggered(); + void on_actionCreateInstanceShortcut_triggered(); + void taskEnd(); /** From 70768189bae0f4d5cfb24b57347cf7207dfc5496 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 22 Oct 2022 17:56:27 +0300 Subject: [PATCH 053/277] Windows: implement FS::createShortcut Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 145 +++++++++++++++++++++++++++++++------ launcher/FileSystem.h | 5 ++ launcher/ui/MainWindow.cpp | 88 +++++++--------------- 3 files changed, 154 insertions(+), 84 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 4a8f4bd3..90f0313f 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -49,6 +49,7 @@ #include "StringUtils.h" #if defined Q_OS_WIN32 +#define WIN32_LEAN_AND_MEAN #include #include #include @@ -339,12 +340,12 @@ QString getDesktopDir() } // Cross-platform Shortcut creation -bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon) +bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - location = PathCombine(location, name + ".desktop"); + destination = PathCombine(destination, name + ".desktop"); - QFile f(location); + QFile f(destination); f.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream stream(&f); @@ -356,10 +357,13 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na << "\n"; stream << "Type=Application" << "\n"; - stream << "TryExec=" << dest.toLocal8Bit() << "\n"; - stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; + stream << "TryExec=" << target.toLocal8Bit() << "\n"; + stream << "Exec=" << target.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n"; - stream << "Icon=" << icon.toLocal8Bit() << "\n"; + if (!icon.isEmpty()) + { + stream << "Icon=" << icon.toLocal8Bit() << "\n"; + } stream.flush(); f.close(); @@ -368,24 +372,121 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na return true; #elif defined Q_OS_WIN - // TODO: Fix - // QFile file(PathCombine(location, name + ".lnk")); - // WCHAR *file_w; - // WCHAR *dest_w; - // WCHAR *args_w; - // file.fileName().toWCharArray(file_w); - // dest.toWCharArray(dest_w); + QFileInfo targetInfo(target); - // QString argStr; - // for (int i = 0; i < args.count(); i++) - // { - // argStr.append(args[i]); - // argStr.append(" "); - // } - // argStr.toWCharArray(args_w); + if (!targetInfo.exists()) + { + qWarning() << "Target file does not exist!"; + return false; + } - // return SUCCEEDED(CreateLink(file_w, dest_w, args_w)); - return false; + target = targetInfo.absoluteFilePath(); + + if (target.length() >= MAX_PATH) + { + qWarning() << "Target file path is too long!"; + return false; + } + + if (!icon.isEmpty() && icon.length() >= MAX_PATH) + { + qWarning() << "Icon path is too long!"; + return false; + } + + destination += ".lnk"; + + if (destination.length() >= MAX_PATH) + { + qWarning() << "Destination path is too long!"; + return false; + } + + QString argStr; + int argCount = args.count(); + for (int i = 0; i < argCount; i++) + { + if (args[i].contains(' ')) + { + argStr.append('"').append(args[i]).append('"'); + } + else + { + argStr.append(args[i]); + } + + if (i < argCount - 1) + { + argStr.append(" "); + } + } + + if (argStr.length() >= MAX_PATH) + { + qWarning() << "Arguments string is too long!"; + return false; + } + + WCHAR wsz[MAX_PATH]; + + // ...yes, you need to initialize the entire COM stack to make a shortcut in Windows + CoInitialize(nullptr); + + HRESULT hres; + IShellLink* psl; + + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); + if (SUCCEEDED(hres)) + { + wmemset(wsz, 0, MAX_PATH); + target.toWCharArray(wsz); + psl->SetPath(wsz); + + wmemset(wsz, 0, MAX_PATH); + argStr.toWCharArray(wsz); + psl->SetArguments(wsz); + + wmemset(wsz, 0, MAX_PATH); + targetInfo.absolutePath().toWCharArray(wsz); + psl->SetWorkingDirectory(wsz); + + if (!icon.isEmpty()) + { + wmemset(wsz, 0, MAX_PATH); + icon.toWCharArray(wsz); + psl->SetIconLocation(wsz, 0); + } + + IPersistFile* ppf; + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); + if (SUCCEEDED(hres)) + { + wmemset(wsz, 0, MAX_PATH); + destination.toWCharArray(wsz); + hres = ppf->Save(wsz, TRUE); + if (FAILED(hres)) + { + qWarning() << "IPresistFile->Save() failed"; + qWarning() << "hres = " << hres; + } + ppf->Release(); + } + else + { + qWarning() << "Failed to query IPersistFile interface from IShellLink instance"; + qWarning() << "hres = " << hres; + } + psl->Release(); + } + else + { + qWarning() << "Failed to create IShellLink instance"; + qWarning() << "hres = " << hres; + } + + CoUninitialize(); + + return SUCCEEDED(hres); #else qWarning("Desktop Shortcuts not supported on your platform!"); return false; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index b7e175fd..23cc575b 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -156,4 +156,9 @@ QString getDesktopDir(); // Overrides one folder with the contents of another, preserving items exclusive to the first folder // Equivalent to doing QDir::rename, but allowing for overrides bool overrideFolder(QString overwritten_path, QString override_path); + +/** + * Creates a shortcut to the specified target file at the specified destination path. + */ +bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon); } diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 419bb9bd..1ad9713a 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -39,6 +39,7 @@ #include "Application.h" #include "BuildConfig.h" +#include "FileSystem.h" #include "MainWindow.h" @@ -739,9 +740,9 @@ public: actionCreateInstanceShortcut = TranslatedAction(MainWindow); actionCreateInstanceShortcut->setObjectName(QStringLiteral("actionCreateInstanceShortcut")); - actionCreateInstanceShortcut.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Create shortcut")); + actionCreateInstanceShortcut.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Create Shortcut")); actionCreateInstanceShortcut.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Creates a shortcut on your desktop to launch the selected instance.")); - actionCreateInstanceShortcut->setShortcut(QKeySequence(tr("Ctrl+D"))); + //actionCreateInstanceShortcut->setShortcut(QKeySequence(tr("Ctrl+D"))); // TODO //actionCreateInstanceShortcut->setIcon(APPLICATION->getThemedIcon("copy")); // TODO all_actions.append(&actionCreateInstanceShortcut); @@ -793,7 +794,7 @@ public: } } - instanceToolBar->addAction(actionCreateInstanceShortcut); + instanceToolBar->addAction(actionCreateInstanceShortcut); // TODO find better position for this all_toolbars.append(&instanceToolBar); MainWindow->addToolBar(Qt::RightToolBarArea, instanceToolBar); @@ -2087,76 +2088,39 @@ void MainWindow::on_actionKillInstance_triggered() } } -#ifdef Q_OS_WIN -#define WIN32_LEAN_AND_MEAN -#include "windows.h" -#include "winnls.h" -#include "shobjidl.h" -#include "objbase.h" -#include "objidl.h" -#include "shlguid.h" -#endif - void MainWindow::on_actionCreateInstanceShortcut_triggered() { if (m_selectedInstance) { - auto desktopDir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); - if (desktopDir.isEmpty()) { + auto desktopPath = FS::getDesktopDir(); + if (desktopPath.isEmpty()) { // TODO come up with an alternative solution (open "save file" dialog) - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop!")); + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop?!")); return; } -#if defined(Q_OS_WIN) - // Windows - WCHAR wsz[MAX_PATH]; - - // ...yes, you need to initialize the entire COM stack to make a shortcut in Windows. - // I hate it. - CoInitialize(nullptr); - - HRESULT hres; - IShellLink* psl; - - hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); - if (SUCCEEDED(hres)) - { - IPersistFile* ppf; - - QApplication::applicationFilePath().left(MAX_PATH - 1).toWCharArray(wsz); - psl->SetPath(wsz); - - wmemset(wsz, 0, MAX_PATH); - QStringLiteral("--launch %1").arg(m_selectedInstance->id()).left(MAX_PATH - 1).toWCharArray(wsz); - psl->SetArguments(wsz); - - // TODO set icon - - hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); - - if (SUCCEEDED(hres)) - { - wmemset(wsz, 0, MAX_PATH); - - desktopDir - .append('/') - .append(QStringLiteral("%1.lnk").arg(m_selectedInstance->name())) - .left(MAX_PATH - 1).toWCharArray(wsz); - - hres = ppf->Save(wsz, TRUE); - ppf->Release(); - } - psl->Release(); - } - - CoUninitialize(); -#elif defined(Q_OS_LINUX) - // Linux -#elif defined(Q_OS_MACOS) +#if defined(Q_OS_MACOS) // macOSX // TODO actually write this path QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOSX yet!")); +#else + QString iconPath; + +#if defined(Q_OS_WIN) + // TODO + // need to convert icon to ICO format and save it somewhere... + iconPath = ""; +#elif defined(Q_OS_UNIX) + iconPath = ""; // TODO get instance icon path +#endif + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), + QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), iconPath)) { + QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); + } + else + { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + } #endif } } From f12117c532de4bdbf74968cf173d1c288a7e426a Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:37:30 +0300 Subject: [PATCH 054/277] [UNTESTED] Linux: add icons to shortcuts Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 1ad9713a..8f2196f2 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2104,6 +2104,8 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() // TODO actually write this path QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOSX yet!")); #else + auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); + QString iconPath; #if defined(Q_OS_WIN) @@ -2111,7 +2113,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() // need to convert icon to ICO format and save it somewhere... iconPath = ""; #elif defined(Q_OS_UNIX) - iconPath = ""; // TODO get instance icon path + iconPath = icon->getFilePath(); #endif if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), iconPath)) { From 6ae3b183fdb1f6e4887617fc7230d52c803e63fd Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Tue, 25 Oct 2022 21:18:53 +0300 Subject: [PATCH 055/277] Remove unnessecary "is defined" check Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 8f2196f2..34c51ec6 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2112,7 +2112,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() // TODO // need to convert icon to ICO format and save it somewhere... iconPath = ""; -#elif defined(Q_OS_UNIX) +#else iconPath = icon->getFilePath(); #endif if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), From b83f9be1bd65f784bbed603e550fbe9f650b0367 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:46:54 +0300 Subject: [PATCH 056/277] Add missing fail check for CoInitialize Add a few comments Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 90f0313f..587753a0 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -427,14 +427,21 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri return false; } + HRESULT hres; + + // ...yes, you need to initialize the entire COM stack just to make a shortcut + hres = CoInitialize(nullptr); + if (FAILED(hres)) + { + qWarning() << "Failed to initialize COM!"; + return false; + } + WCHAR wsz[MAX_PATH]; - // ...yes, you need to initialize the entire COM stack to make a shortcut in Windows - CoInitialize(nullptr); - - HRESULT hres; IShellLink* psl; + // create an IShellLink instance - this stores the shortcut's attributes hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); if (SUCCEEDED(hres)) { @@ -448,7 +455,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri wmemset(wsz, 0, MAX_PATH); targetInfo.absolutePath().toWCharArray(wsz); - psl->SetWorkingDirectory(wsz); + psl->SetWorkingDirectory(wsz); // "Starts in" attribute if (!icon.isEmpty()) { @@ -457,6 +464,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri psl->SetIconLocation(wsz, 0); } + // query an IPersistFile interface from our IShellLink instance + // this is the interface that will actually let us save the shortcut to disk! IPersistFile* ppf; hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); if (SUCCEEDED(hres)) @@ -484,6 +493,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri qWarning() << "hres = " << hres; } + // go away COM, nobody likes you CoUninitialize(); return SUCCEEDED(hres); From 4d4dfab38869bca82626e171336cbe8f7c89a1a1 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:42:29 +0300 Subject: [PATCH 057/277] Windows: add instance icon to shortcut Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 34c51ec6..9743c822 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2107,11 +2107,28 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); QString iconPath; + bool iconGenerated = false; #if defined(Q_OS_WIN) - // TODO - // need to convert icon to ICO format and save it somewhere... - iconPath = ""; + iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); + + QFile iconFile(iconPath); + if (!iconFile.open(QFile::WriteOnly)) + { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + return; + } + + if (!icon->icon().pixmap(64, 64).save(&iconFile, "ICO")) + { + iconFile.close(); + iconFile.remove(); + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + return; + } + + iconFile.close(); + iconGenerated = true; #else iconPath = icon->getFilePath(); #endif @@ -2121,6 +2138,10 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } else { + if (iconGenerated) + { + QFile::remove(iconPath); + } QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); } #endif From cc5f82bfac162becf55dfcb015f55be3c833bc32 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Fri, 28 Oct 2022 21:11:51 +0300 Subject: [PATCH 058/277] Windows: fix window icon being replaced Dunno why it happens but the fix was literally 2 lines, so whatever I guess Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 9743c822..a87da362 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2112,6 +2112,10 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() #if defined(Q_OS_WIN) iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); + // part of fix for weird bug involving the window icon being replaced + // dunno why it happens, but this 2-line fix seems to be enough, so w/e + auto appIcon = QGuiApplication::windowIcon(); + QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { @@ -2129,6 +2133,9 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() iconFile.close(); iconGenerated = true; + + // restore original window icon + QGuiApplication::setWindowIcon(appIcon); #else iconPath = icon->getFilePath(); #endif From 487e352642c4430f57643b53a8e9cd3d04edfeb8 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Fri, 4 Nov 2022 22:04:42 +0200 Subject: [PATCH 059/277] Fix nested #if directive Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index a87da362..15fdd6f2 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2103,7 +2103,8 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() // macOSX // TODO actually write this path QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOSX yet!")); -#else + return; +#endif auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); QString iconPath; @@ -2151,7 +2152,6 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); } -#endif } } From 6043444e4e11801e45ad888182c99d6f4e4e5ddc Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Wed, 9 Nov 2022 21:02:40 +0200 Subject: [PATCH 060/277] Apply suggestions from code review Co-authored-by: Sefa Eyeoglu Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 2 +- launcher/ui/MainWindow.cpp | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 587753a0..5a539093 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -342,7 +342,7 @@ QString getDesktopDir() // Cross-platform Shortcut creation bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) destination = PathCombine(destination, name + ".desktop"); QFile f(destination); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 15fdd6f2..cd3c1b5b 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2099,23 +2099,21 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() return; } -#if defined(Q_OS_MACOS) - // macOSX - // TODO actually write this path - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOSX yet!")); - return; +#ifdef Q_OS_MACOS + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOS yet!")); + return; #endif auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); QString iconPath; bool iconGenerated = false; -#if defined(Q_OS_WIN) +#ifdef Q_OS_WIN iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); // part of fix for weird bug involving the window icon being replaced // dunno why it happens, but this 2-line fix seems to be enough, so w/e - auto appIcon = QGuiApplication::windowIcon(); + auto appIcon = Application::getThemedIcon("logo"); QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) From 9be48f1dc44dc764d2131c559780624908ff636d Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Wed, 9 Nov 2022 21:55:13 +0200 Subject: [PATCH 061/277] this ain't static, falco Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index cd3c1b5b..ff1982ce 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2113,7 +2113,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() // part of fix for weird bug involving the window icon being replaced // dunno why it happens, but this 2-line fix seems to be enough, so w/e - auto appIcon = Application::getThemedIcon("logo"); + auto appIcon = APPLICATION->getThemedIcon("logo"); QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) @@ -2121,20 +2121,22 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); return; } + bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO"); + iconFile.close(); - if (!icon->icon().pixmap(64, 64).save(&iconFile, "ICO")) + // restore original window icon + QGuiApplication::setWindowIcon(appIcon); + + if (success) + { + iconGenerated = true; + } + else { - iconFile.close(); iconFile.remove(); QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); return; } - - iconFile.close(); - iconGenerated = true; - - // restore original window icon - QGuiApplication::setWindowIcon(appIcon); #else iconPath = icon->getFilePath(); #endif From dbe0553b4a6100d8269f749cc4eb372e5a30cb3b Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Wed, 9 Nov 2022 21:56:46 +0200 Subject: [PATCH 062/277] Move addAction call above left-align loop Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index ff1982ce..0a846b59 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -784,6 +784,8 @@ public: instanceToolBar->addAction(actionCopyInstance); instanceToolBar->addAction(actionDeleteInstance); + instanceToolBar->addAction(actionCreateInstanceShortcut); // TODO find better position for this + QLayout * lay = instanceToolBar->layout(); for(int i = 0; i < lay->count(); i++) { @@ -794,8 +796,6 @@ public: } } - instanceToolBar->addAction(actionCreateInstanceShortcut); // TODO find better position for this - all_toolbars.append(&instanceToolBar); MainWindow->addToolBar(Qt::RightToolBarArea, instanceToolBar); } From 7e5076b06891f28c1b2e27befd33005d400c49c9 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 12 Nov 2022 20:13:59 +0200 Subject: [PATCH 063/277] Linux: fix path shortcut is written to Co-authored-by: flow Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 5a539093..ddd1a6e5 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -343,7 +343,7 @@ QString getDesktopDir() bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - destination = PathCombine(destination, name + ".desktop"); + destination += ".desktop"; QFile f(destination); f.open(QIODevice::WriteOnly | QIODevice::Text); From f7d7d76ee879c3bdd539e5c8c956cbd2c7328bf0 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 12 Nov 2022 20:36:49 +0200 Subject: [PATCH 064/277] Mac: now supported! [UNTESTED] Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 26 +++++++++++++++++++++++++- launcher/ui/MainWindow.cpp | 15 ++++++++++++++- libraries/tomlplusplus | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index ddd1a6e5..221395be 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -342,7 +342,31 @@ QString getDesktopDir() // Cross-platform Shortcut creation bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) +#if defined(Q_OS_MACOS) + destination += ".sh"; + + QFile f(destination); + f.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream stream(&f); + + QString argstring; + if (!args.empty()) + argstring = " \"" + args.join("\" \"") + "\""; + + stream << "#!/bin/bash" + << "\n"; + stream << target + << " " + << argstring + << "\n"; + + stream.flush(); + f.close(); + + f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); + + return true; +#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) destination += ".desktop"; QFile f(destination); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 0a846b59..02f60233 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2100,7 +2100,20 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } #ifdef Q_OS_MACOS - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOS yet!")); + // handle macOS bundle weirdness + QFileInfo appFileInfo(QApplication::applicationFilePath())); + QString appName = appFileInfo.baseName(); + QString exeName = FS::PathCombine(appFileInfo.filePath(), "Contents/MacOS/" + appName); + + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), + exeName, { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), "")) { + QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); + } + else + { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + } + return; #endif auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); diff --git a/libraries/tomlplusplus b/libraries/tomlplusplus index cc741c9f..4b166b69 160000 --- a/libraries/tomlplusplus +++ b/libraries/tomlplusplus @@ -1 +1 @@ -Subproject commit cc741c9f5f2a62856a2a2e9e275f61eb0591c09c +Subproject commit 4b166b69f28e70a416a1a04a98f365d2aeb90de8 From b813c867b505f0c1ec8125fde9916d6252cd4485 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 12 Nov 2022 20:41:52 +0200 Subject: [PATCH 065/277] refactor #if checks Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 2 +- launcher/ui/MainWindow.cpp | 46 ++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 221395be..7a1861e7 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -395,7 +395,7 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); return true; -#elif defined Q_OS_WIN +#elif defined(Q_OS_WIN) QFileInfo targetInfo(target); if (!targetInfo.exists()) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 02f60233..8e8a7c56 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2099,13 +2099,13 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() return; } -#ifdef Q_OS_MACOS +#if defined(Q_OS_MACOS) // handle macOS bundle weirdness QFileInfo appFileInfo(QApplication::applicationFilePath())); QString appName = appFileInfo.baseName(); QString exeName = FS::PathCombine(appFileInfo.filePath(), "Contents/MacOS/" + appName); - if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), exeName, { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), "")) { QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); } @@ -2113,17 +2113,22 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() { QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); } - - return; -#endif +#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); - - QString iconPath; - bool iconGenerated = false; - -#ifdef Q_OS_WIN - iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); - + + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), + QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), icon->getFilePath())) { + QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); + } + else + { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + } +#elif defined(Q_OS_WIN) + auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); + + QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); + // part of fix for weird bug involving the window icon being replaced // dunno why it happens, but this 2-line fix seems to be enough, so w/e auto appIcon = APPLICATION->getThemedIcon("logo"); @@ -2140,31 +2145,24 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() // restore original window icon QGuiApplication::setWindowIcon(appIcon); - if (success) - { - iconGenerated = true; - } - else + if (!success) { iconFile.remove(); QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); return; } -#else - iconPath = icon->getFilePath(); -#endif + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), iconPath)) { QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); } else { - if (iconGenerated) - { - QFile::remove(iconPath); - } QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); } +#else + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on your platform!")); +#endif } } From 45ce72593ac29e54311219e71d47b044eda14b14 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 12 Nov 2022 20:43:58 +0200 Subject: [PATCH 066/277] Windows: remove icon if shortcut creation fails Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 8e8a7c56..3f99ac99 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2158,6 +2158,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } else { + iconFile.remove(); QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); } #else From 5322155b19e68b44e879512cf46ee152045dc1b1 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 12 Nov 2022 20:48:21 +0200 Subject: [PATCH 067/277] Mac: fix build Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 3f99ac99..aedd9e4f 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2101,7 +2101,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() #if defined(Q_OS_MACOS) // handle macOS bundle weirdness - QFileInfo appFileInfo(QApplication::applicationFilePath())); + QFileInfo appFileInfo(QApplication::applicationFilePath()); QString appName = appFileInfo.baseName(); QString exeName = FS::PathCombine(appFileInfo.filePath(), "Contents/MacOS/" + appName); From ddfb449b28fb24f1c3e4ed3802ee4415206f96f1 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 22 Oct 2022 20:01:39 +0200 Subject: [PATCH 068/277] fix: remove PolyMC data paths Signed-off-by: Sefa Eyeoglu --- launcher/Application.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 45cd9422..e2fdcd63 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -301,22 +301,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; - QDir polymcData(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "PolyMC")); - if (polymcData.exists()) { - dataPath = polymcData.absolutePath(); - adjustedBy = "PolyMC data path"; - } - -#ifdef Q_OS_LINUX - // TODO: this should be removed in a future version - // TODO: provide a migration path similar to macOS migration - QDir bar(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "polymc")); - if (bar.exists()) { - dataPath = bar.absolutePath(); - adjustedBy = "Legacy data path"; - } -#endif - #ifndef Q_OS_MACOS if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { dataPath = m_rootPath; From e048bce13ea4bd56ef96ba7a1a4699142d09600a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 22 Oct 2022 23:25:14 +0200 Subject: [PATCH 069/277] refactor: allow copy operation with whitelist Signed-off-by: Sefa Eyeoglu --- launcher/FileSystem.cpp | 2 +- launcher/FileSystem.h | 12 +++++++++--- launcher/InstanceCopyTask.cpp | 2 +- tests/FileSystem_test.cpp | 37 ++++++++++++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 4a8f4bd3..a3b9fe1f 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -174,7 +174,7 @@ bool copy::operator()(const QString& offset) // 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)) + if (m_matcher && (m_matcher->matches(relative_dst_path) == !m_whitelist)) return; auto dst_path = PathCombine(dst, relative_dst_path); diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index b7e175fd..e239984e 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -88,9 +88,14 @@ class copy { m_followSymlinks = follow; return *this; } - copy& blacklist(const IPathMatcher* filter) + copy& matcher(const IPathMatcher* filter) { - m_blacklist = filter; + m_matcher = filter; + return *this; + } + copy& whitelist(bool whitelist) + { + m_whitelist = whitelist; return *this; } bool operator()() { return operator()(QString()); } @@ -100,7 +105,8 @@ class copy { private: bool m_followSymlinks = true; - const IPathMatcher* m_blacklist = nullptr; + const IPathMatcher* m_matcher = nullptr; + bool m_whitelist = false; QDir m_src; QDir m_dst; }; diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index a4ea947d..fb118353 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -26,7 +26,7 @@ void InstanceCopyTask::executeTask() setStatus(tr("Copying instance %1").arg(m_origInstance->name())); FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); - folderCopy.followSymlinks(false).blacklist(m_matcher.get()); + folderCopy.followSymlinks(false).matcher(m_matcher.get()); m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished); diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp index 21270f6f..3a5c38d0 100644 --- a/tests/FileSystem_test.cpp +++ b/tests/FileSystem_test.cpp @@ -126,7 +126,7 @@ slots: qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(folder, target_dir.path()); - c.blacklist(new RegexpMatcher("[.]?mcmeta")); + c.matcher(new RegexpMatcher("[.]?mcmeta")); c(); for(auto entry: target_dir.entryList()) @@ -147,6 +147,41 @@ slots: f(); } + void test_copy_with_whitelist() + { + QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); + auto f = [&folder]() + { + QTemporaryDir tempDir; + tempDir.setAutoRemove(true); + qDebug() << "From:" << folder << "To:" << tempDir.path(); + + QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); + qDebug() << tempDir.path(); + qDebug() << target_dir.path(); + FS::copy c(folder, target_dir.path()); + c.matcher(new RegexpMatcher("[.]?mcmeta")); + c.whitelist(true); + c(); + + for(auto entry: target_dir.entryList()) + { + qDebug() << entry; + } + QVERIFY(target_dir.entryList().contains("pack.mcmeta")); + QVERIFY(!target_dir.entryList().contains("assets")); + }; + + // first try variant without trailing / + QVERIFY(!folder.endsWith('/')); + f(); + + // then variant with trailing / + folder.append('/'); + QVERIFY(folder.endsWith('/')); + f(); + } + void test_copy_with_dot_hidden() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); From 15aaff7c1ce8d709c444d891bf640ee39494d10e Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 22 Oct 2022 23:36:47 +0200 Subject: [PATCH 070/277] feat: add dryRun to copy operation Signed-off-by: Sefa Eyeoglu --- launcher/FileSystem.cpp | 10 ++++++---- launcher/FileSystem.h | 18 ++++++++++++++---- launcher/InstanceCopyTask.cpp | 4 +++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index a3b9fe1f..06691fbf 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -152,9 +152,10 @@ bool ensureFolderPathExists(QString foldernamepath) /// @brief Copies a directory and it's contents from src to dest /// @param offset subdirectory form src to copy to dest /// @return if there was an error during the filecopy -bool copy::operator()(const QString& offset) +bool copy::operator()(const QString& offset, bool dryRun) { using copy_opts = fs::copy_options; + m_copied = 0; // reset counter // NOTE always deep copy on windows. the alternatives are too messy. #if defined Q_OS_WIN32 @@ -178,9 +179,10 @@ bool copy::operator()(const QString& offset) return; auto dst_path = PathCombine(dst, relative_dst_path); - ensureFilePathExists(dst_path); - - fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err); + if (!dryRun) { + ensureFilePathExists(dst_path); + fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err); + } if (err) { qWarning() << "Failed to copy files:" << QString::fromStdString(err.message()); qDebug() << "Source file:" << src_path; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index e239984e..a9a81123 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -40,6 +40,7 @@ #include #include +#include namespace FS { @@ -76,9 +77,10 @@ bool ensureFilePathExists(QString filenamepath); bool ensureFolderPathExists(QString filenamepath); /// @brief Copies a directory and it's contents from src to dest -class copy { +class copy : public QObject { + Q_OBJECT public: - copy(const QString& src, const QString& dst) + copy(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent) { m_src.setPath(src); m_dst.setPath(dst); @@ -98,10 +100,17 @@ class copy { m_whitelist = whitelist; return *this; } - bool operator()() { return operator()(QString()); } + + bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } + + int totalCopied() { return m_copied; } + + signals: + void fileCopied(const QString& relativeName); + // TODO: maybe add a "shouldCopy" signal in the future? private: - bool operator()(const QString& offset); + bool operator()(const QString& offset, bool dryRun = false); private: bool m_followSymlinks = true; @@ -109,6 +118,7 @@ class copy { bool m_whitelist = false; QDir m_src; QDir m_dst; + int m_copied; }; /** diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index fb118353..0a83ed9c 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -28,7 +28,9 @@ void InstanceCopyTask::executeTask() FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); folderCopy.followSymlinks(false).matcher(m_matcher.get()); - m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy); + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&folderCopy]{ + return folderCopy(); + }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &InstanceCopyTask::copyAborted); m_copyFutureWatcher.setFuture(m_copyFuture); From bd7065eece443de59adbe47dd7d9bd16e1d35ff5 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 23 Oct 2022 00:44:57 +0200 Subject: [PATCH 071/277] feat: add SimplePrefixMatcher Signed-off-by: Sefa Eyeoglu --- launcher/CMakeLists.txt | 1 + launcher/pathmatcher/SimplePrefixMatcher.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 launcher/pathmatcher/SimplePrefixMatcher.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 8db93429..45d197ef 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -97,6 +97,7 @@ set(PATHMATCHER_SOURCES pathmatcher/IPathMatcher.h pathmatcher/MultiMatcher.h pathmatcher/RegexpMatcher.h + pathmatcher/SimplePrefixMatcher.h ) set(NET_SOURCES diff --git a/launcher/pathmatcher/SimplePrefixMatcher.h b/launcher/pathmatcher/SimplePrefixMatcher.h new file mode 100644 index 00000000..191d010c --- /dev/null +++ b/launcher/pathmatcher/SimplePrefixMatcher.h @@ -0,0 +1,21 @@ +#include +#include "IPathMatcher.h" + +class SimplePrefixMatcher : public IPathMatcher { + public: + virtual ~SimplePrefixMatcher(){}; + SimplePrefixMatcher(const QString& prefix) + { + m_prefix = prefix; + m_isPrefix = prefix.endsWith('/'); + } + + virtual bool matches(const QString& string) const override + { + if (m_isPrefix) + return string.startsWith(m_prefix); + return string == m_prefix; + } + QString m_prefix; + bool m_isPrefix = false; +}; From 086304f7f24e70bfa35b26a7406930b0840f699b Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 23 Oct 2022 01:45:32 +0200 Subject: [PATCH 072/277] feat: add initial Migration dialog Signed-off-by: Sefa Eyeoglu --- launcher/Application.cpp | 99 ++++++++++++++++++++++++++++++++++ launcher/Application.h | 1 + launcher/CMakeLists.txt | 2 + launcher/DataMigrationTask.cpp | 79 +++++++++++++++++++++++++++ launcher/DataMigrationTask.h | 38 +++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 launcher/DataMigrationTask.cpp create mode 100644 launcher/DataMigrationTask.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e2fdcd63..2a7d6f22 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -38,10 +38,14 @@ #include "Application.h" #include "BuildConfig.h" +#include "DataMigrationTask.h" #include "net/PasteUpload.h" +#include "pathmatcher/MultiMatcher.h" +#include "pathmatcher/SimplePrefixMatcher.h" #include "ui/MainWindow.h" #include "ui/InstanceWindow.h" +#include "ui/dialogs/ProgressDialog.h" #include "ui/instanceview/AccessibleInstanceView.h" #include "ui/pages/BasePageProvider.h" @@ -423,6 +427,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) qDebug() << "<> Log initialized."; } + { + bool migrated = false; + + if (!migrated) + migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", "polymc.cfg"); + if (!migrated) + migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", "multimc.cfg"); + } + { qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; @@ -1589,3 +1602,89 @@ int Application::suitableMaxMem() return maxMemoryAlloc; } + +bool Application::handleDataMigration(const QString& currentData, + const QString& oldData, + const QString& name, + const QString& configFile) const +{ + QString nomigratePath = FS::PathCombine(oldData, BuildConfig.LAUNCHER_NAME + "_nomigrate.txt"); + QStringList configPaths = { FS::PathCombine(oldData, configFile), FS::PathCombine(oldData, BuildConfig.LAUNCHER_CONFIGFILE) }; + + QDir dir; // helper for QDir::exists + QLocale locale; + + // Is there a valid config at the old location? + bool configExists = false; + for (QString configPath : configPaths) { + configExists |= QFileInfo::exists(configPath); + } + + if (!configExists || QFileInfo::exists(nomigratePath)) { + qDebug() << "<> No migration needed from" << name; + return false; + } + + QString message; + bool currentExists = QFileInfo::exists(FS::PathCombine(currentData, BuildConfig.LAUNCHER_CONFIGFILE)); + + if (currentExists) { + message = tr("Old data from %1 was found, but you already have existing data for %2. Sadly you will need to migrate yourself. Do " + "you want to be reminded of the pending data migration next time you start %2?") + .arg(name, BuildConfig.LAUNCHER_DISPLAYNAME); + } else { + message = tr("It looks like you used %1 before. Do you want to migrate your data to the new location of %2?") + .arg(name, BuildConfig.LAUNCHER_DISPLAYNAME); + + QFileInfo logInfo(FS::PathCombine(oldData, name + "-0.log")); + if (logInfo.exists()) { + QString lastModified = logInfo.lastModified().toString(locale.dateFormat()); + message = tr("It looks like you used %1 on %2 before. Do you want to migrate your data to the new location of %3?") + .arg(name, lastModified, BuildConfig.LAUNCHER_DISPLAYNAME); + } + } + + QMessageBox::StandardButton askMoveDialogue = + QMessageBox::question(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + auto setDoNotMigrate = [&nomigratePath] { + QFile file(nomigratePath); + file.open(QIODevice::WriteOnly); + }; + + // create no-migrate file if user doesn't want to migrate + if (askMoveDialogue != QMessageBox::Yes) { + qDebug() << "<> Migration declined for" << name; + setDoNotMigrate(); + return currentExists; // cancel further migrations, if we already have a data directory + } + + if (!currentExists) { + // Migrate! + auto matcher = std::make_shared(); + matcher->add(std::make_shared(configFile)); + matcher->add(std::make_shared( + BuildConfig.LAUNCHER_CONFIGFILE)); // it's possible that we already used that directory before + matcher->add(std::make_shared("accounts.json")); + matcher->add(std::make_shared("accounts/")); + matcher->add(std::make_shared("assets/")); + matcher->add(std::make_shared("icons/")); + matcher->add(std::make_shared("instances/")); + matcher->add(std::make_shared("libraries/")); + matcher->add(std::make_shared("mods/")); + matcher->add(std::make_shared("themes/")); + + ProgressDialog diag = ProgressDialog(); + DataMigrationTask task(nullptr, oldData, currentData, matcher); + if (diag.execWithTask(&task)) { + qDebug() << "<> Migration succeeded"; + setDoNotMigrate(); + } else { + QString reason = task.failReason(); + QMessageBox::critical(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, tr("Migration failed! Reason: %1").arg(reason)); + } + } else { + qWarning() << "<> Migration was skipped, due to existing data"; + } + return true; +} diff --git a/launcher/Application.h b/launcher/Application.h index 4c2f62d4..7884227a 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -231,6 +231,7 @@ private slots: void setupWizardFinished(int status); private: + bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const; bool createSetupWizard(); void performMainStartupAction(); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 45d197ef..7a577935 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -576,6 +576,8 @@ SET(LAUNCHER_SOURCES # Application base Application.h Application.cpp + DataMigrationTask.h + DataMigrationTask.cpp UpdateController.cpp UpdateController.h ApplicationMessage.h diff --git a/launcher/DataMigrationTask.cpp b/launcher/DataMigrationTask.cpp new file mode 100644 index 00000000..8e7f4579 --- /dev/null +++ b/launcher/DataMigrationTask.cpp @@ -0,0 +1,79 @@ +#include "DataMigrationTask.h" + +#include "FileSystem.h" + +#include +#include +#include + +#include + +DataMigrationTask::DataMigrationTask(QObject* parent, + const QString& sourcePath, + const QString& targetPath, + const IPathMatcher::Ptr pathMatcher) + : Task(parent), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath) +{ + m_copy.matcher(m_pathMatcher.get()).whitelist(true); +} + +void DataMigrationTask::executeTask() +{ + setStatus(tr("Scanning files...")); + + // 1. Scan + // Check how many files we gotta copy + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] { + return m_copy(true); // dry run to collect amount of files + }); + connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::dryRunFinished); + connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::dryRunAborted); + m_copyFutureWatcher.setFuture(m_copyFuture); +} + +void DataMigrationTask::dryRunFinished() +{ + disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::dryRunFinished); + disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::dryRunAborted); + + if (!m_copyFuture.result()) { + emitFailed("Some error"); // FIXME + return; + } + + setStatus(tr("Migrating...")); + + // 2. Copy + // Actually copy all files now. + m_toCopy = m_copy.totalCopied(); + connect(&m_copy, &FS::copy::fileCopied, [&, this] { setProgress(m_copy.totalCopied(), m_toCopy); }); + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] { + return m_copy(false); // actually copy now + }); + connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::copyFinished); + connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::copyAborted); + m_copyFutureWatcher.setFuture(m_copyFuture); +} + +void DataMigrationTask::dryRunAborted() +{ + emitFailed(tr("Aborted")); +} + +void DataMigrationTask::copyFinished() +{ + disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::copyFinished); + disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::copyAborted); + + if (!m_copyFuture.result()) { + emitFailed("Some paths could not be copied!"); + return; + } + + emitSucceeded(); +} + +void DataMigrationTask::copyAborted() +{ + emitFailed(tr("Aborted")); +} diff --git a/launcher/DataMigrationTask.h b/launcher/DataMigrationTask.h new file mode 100644 index 00000000..105a9493 --- /dev/null +++ b/launcher/DataMigrationTask.h @@ -0,0 +1,38 @@ +#pragma once + +#include "FileSystem.h" +#include "pathmatcher/IPathMatcher.h" +#include "tasks/Task.h" + +#include +#include + +/* + * Migrate existing data from other MMC-like launchers. + */ + +class DataMigrationTask : public Task { + Q_OBJECT + public: + explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathmatcher); + ~DataMigrationTask() override = default; + + protected: + virtual void executeTask() override; + + protected slots: + void dryRunFinished(); + void dryRunAborted(); + void copyFinished(); + void copyAborted(); + + private: + const QString& m_sourcePath; + const QString& m_targetPath; + const IPathMatcher::Ptr m_pathMatcher; + + FS::copy m_copy; + int m_toCopy = 0; + QFuture m_copyFuture; + QFutureWatcher m_copyFutureWatcher; +}; From bbb7e9f5c722039ba8b4fbd00fba78f65613b0a9 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 23 Oct 2022 14:24:11 +0200 Subject: [PATCH 073/277] feat: show current copy operation in migration dialog Signed-off-by: Sefa Eyeoglu --- launcher/DataMigrationTask.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/launcher/DataMigrationTask.cpp b/launcher/DataMigrationTask.cpp index 8e7f4579..fb2907fb 100644 --- a/launcher/DataMigrationTask.cpp +++ b/launcher/DataMigrationTask.cpp @@ -41,12 +41,17 @@ void DataMigrationTask::dryRunFinished() return; } - setStatus(tr("Migrating...")); - // 2. Copy // Actually copy all files now. m_toCopy = m_copy.totalCopied(); - connect(&m_copy, &FS::copy::fileCopied, [&, this] { setProgress(m_copy.totalCopied(), m_toCopy); }); + connect(&m_copy, &FS::copy::fileCopied, [&, this](const QString& relativeName) { + QString shortenedName = relativeName; + // shorten the filename to hopefully fit into one line + if (shortenedName.length() > 50) + shortenedName = relativeName.left(20) + "…" + relativeName.right(29); + setProgress(m_copy.totalCopied(), m_toCopy); + setStatus(tr("Copying %1…").arg(shortenedName)); + }); m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] { return m_copy(false); // actually copy now }); From 335bec68fb803f0a06400585b1dc4c2341951c7c Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 23 Oct 2022 14:27:46 +0200 Subject: [PATCH 074/277] fix: prevent abort for un-abortable tasks Signed-off-by: Sefa Eyeoglu --- launcher/ui/dialogs/ProgressDialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index 05269f62..da73a449 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -44,7 +44,8 @@ void ProgressDialog::setSkipButton(bool present, QString label) void ProgressDialog::on_skipButton_clicked(bool checked) { Q_UNUSED(checked); - task->abort(); + if (ui->skipButton->isEnabled()) // prevent other triggers from aborting + task->abort(); } ProgressDialog::~ProgressDialog() From 173aed7fd8e73b9e6a6055981ce284ea9cf5d33a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 24 Oct 2022 21:50:35 +0200 Subject: [PATCH 075/277] chore: add REUSE headers Signed-off-by: Sefa Eyeoglu --- launcher/Application.cpp | 5 ++++- launcher/DataMigrationTask.cpp | 4 ++++ launcher/DataMigrationTask.h | 4 ++++ launcher/pathmatcher/SimplePrefixMatcher.h | 4 ++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 2a7d6f22..8955e297 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1,4 +1,7 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 + /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu diff --git a/launcher/DataMigrationTask.cpp b/launcher/DataMigrationTask.cpp index fb2907fb..8de3158e 100644 --- a/launcher/DataMigrationTask.cpp +++ b/launcher/DataMigrationTask.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only + #include "DataMigrationTask.h" #include "FileSystem.h" diff --git a/launcher/DataMigrationTask.h b/launcher/DataMigrationTask.h index 105a9493..6cc23b1a 100644 --- a/launcher/DataMigrationTask.h +++ b/launcher/DataMigrationTask.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only + #pragma once #include "FileSystem.h" diff --git a/launcher/pathmatcher/SimplePrefixMatcher.h b/launcher/pathmatcher/SimplePrefixMatcher.h index 191d010c..fc1f5ced 100644 --- a/launcher/pathmatcher/SimplePrefixMatcher.h +++ b/launcher/pathmatcher/SimplePrefixMatcher.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu +// +// SPDX-License-Identifier: GPL-3.0-only + #include #include "IPathMatcher.h" From fe94c3609ef875166b71b9f6c540c45eff97a5ab Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 10 Nov 2022 19:04:42 +0100 Subject: [PATCH 076/277] fix: implement code review suggestions Signed-off-by: Sefa Eyeoglu --- launcher/Application.cpp | 5 ++--- launcher/DataMigrationTask.cpp | 12 ++++++++++-- launcher/FileSystem.cpp | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 8955e297..537e3903 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1611,10 +1611,9 @@ bool Application::handleDataMigration(const QString& currentData, const QString& name, const QString& configFile) const { - QString nomigratePath = FS::PathCombine(oldData, BuildConfig.LAUNCHER_NAME + "_nomigrate.txt"); + QString nomigratePath = FS::PathCombine(currentData, name + "_nomigrate.txt"); QStringList configPaths = { FS::PathCombine(oldData, configFile), FS::PathCombine(oldData, BuildConfig.LAUNCHER_CONFIGFILE) }; - QDir dir; // helper for QDir::exists QLocale locale; // Is there a valid config at the old location? @@ -1677,7 +1676,7 @@ bool Application::handleDataMigration(const QString& currentData, matcher->add(std::make_shared("mods/")); matcher->add(std::make_shared("themes/")); - ProgressDialog diag = ProgressDialog(); + ProgressDialog diag; DataMigrationTask task(nullptr, oldData, currentData, matcher); if (diag.execWithTask(&task)) { qDebug() << "<> Migration succeeded"; diff --git a/launcher/DataMigrationTask.cpp b/launcher/DataMigrationTask.cpp index 8de3158e..27ce5f01 100644 --- a/launcher/DataMigrationTask.cpp +++ b/launcher/DataMigrationTask.cpp @@ -40,8 +40,12 @@ void DataMigrationTask::dryRunFinished() disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::dryRunFinished); disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::dryRunAborted); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (!m_copyFuture.isValid() || !m_copyFuture.result()) { +#else if (!m_copyFuture.result()) { - emitFailed("Some error"); // FIXME +#endif + emitFailed(tr("Failed to scan source path.")); return; } @@ -74,8 +78,12 @@ void DataMigrationTask::copyFinished() disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::copyFinished); disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::copyAborted); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (!m_copyFuture.isValid() || !m_copyFuture.result()) { +#else if (!m_copyFuture.result()) { - emitFailed("Some paths could not be copied!"); +#endif + emitFailed(tr("Some paths could not be copied!")); return; } diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 06691fbf..0c6527b1 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -175,7 +175,7 @@ bool copy::operator()(const QString& offset, bool dryRun) // Function that'll do the actual copying auto copy_file = [&](QString src_path, QString relative_dst_path) { - if (m_matcher && (m_matcher->matches(relative_dst_path) == !m_whitelist)) + if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) return; auto dst_path = PathCombine(dst, relative_dst_path); From 577069cfb4982735c038607c06245e6939d7be79 Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 12 Nov 2022 19:23:57 -0300 Subject: [PATCH 077/277] fix: don't have the clear button on instance page filters This thing is otherworldly unoptimized. o.O Signed-off-by: flow --- launcher/ui/pages/instance/ExternalResourcesPage.ui | 6 +----- launcher/ui/pages/instance/VersionPage.ui | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 76f8ec18..33a03336 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -27,11 +27,7 @@ - - - true - - + diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui index fcba5598..14b7cd9f 100644 --- a/launcher/ui/pages/instance/VersionPage.ui +++ b/launcher/ui/pages/instance/VersionPage.ui @@ -48,11 +48,7 @@ - - - true - - + From 69bbb2932848fe7509f91623bac2a648ce594ad7 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sun, 13 Nov 2022 15:47:37 +0200 Subject: [PATCH 078/277] Mac: attempt 2 - create .command files instead of .sh - fix shortcuts not working if path to Prism Launcher contains spaces - fix path to executable in shortcutss - add check for running from extracted folder (prevents creating shortcuts) Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 7 ++++--- launcher/ui/MainWindow.cpp | 11 ++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 7a1861e7..80715498 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -343,7 +343,7 @@ QString getDesktopDir() bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { #if defined(Q_OS_MACOS) - destination += ".sh"; + destination += ".command"; QFile f(destination); f.open(QIODevice::WriteOnly | QIODevice::Text); @@ -355,8 +355,9 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri stream << "#!/bin/bash" << "\n"; - stream << target - << " " + stream << "\"" + << target + << "\" " << argstring << "\n"; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index aedd9e4f..17371149 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2100,13 +2100,14 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } #if defined(Q_OS_MACOS) - // handle macOS bundle weirdness - QFileInfo appFileInfo(QApplication::applicationFilePath()); - QString appName = appFileInfo.baseName(); - QString exeName = FS::PathCombine(appFileInfo.filePath(), "Contents/MacOS/" + appName); + QString appPath = QApplication::applicationFilePath(); + if (appPath.startsWith("/private/var")) { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); + return; + } if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), - exeName, { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), "")) { + appPath, { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), "")) { QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); } else From 43b9d9484da280fc209a0c9f195b0ca338eacdb9 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sun, 13 Nov 2022 15:49:28 +0200 Subject: [PATCH 079/277] tabs -> spaces Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 17371149..c7de46c7 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2101,10 +2101,10 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() #if defined(Q_OS_MACOS) QString appPath = QApplication::applicationFilePath(); - if (appPath.startsWith("/private/var")) { + if (appPath.startsWith("/private/var")) { QMessageBox::critical(this, tr("Create instance shortcut"), tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); return; - } + } if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), appPath, { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), "")) { From b0269e6dfc685cd002340e95e80942410f1b1fc5 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sun, 13 Nov 2022 17:51:29 +0200 Subject: [PATCH 080/277] Linux: fixes - Fix shortcut icons - Possibly fix shortcut creation on AppImages - Fix shortcut not working if path to launcher contains spaces Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 4 ++-- launcher/ui/MainWindow.cpp | 48 +++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 80715498..e1059ca9 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -382,8 +382,8 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri << "\n"; stream << "Type=Application" << "\n"; - stream << "TryExec=" << target.toLocal8Bit() << "\n"; - stream << "Exec=" << target.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; + stream << "TryExec=\"" << target.toLocal8Bit() << "\"\n"; + stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n"; if (!icon.isEmpty()) { diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index c7de46c7..4dbac967 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2101,13 +2101,14 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() #if defined(Q_OS_MACOS) QString appPath = QApplication::applicationFilePath(); - if (appPath.startsWith("/private/var")) { + if (appPath.startsWith("/private/var/")) { QMessageBox::critical(this, tr("Create instance shortcut"), tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); return; } if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), - appPath, { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), "")) { + appPath, { "--launch", m_selectedInstance->id() }, + m_selectedInstance->name(), "")) { QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); } else @@ -2115,14 +2116,48 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); } #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) + QString appPath = QApplication::applicationFilePath(); + if (appPath.startsWith("/tmp/.mount_")) { + // AppImage! + appPath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); + if (appPath.isEmpty()) + { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Launcher is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); + } + else if (appPath.endsWith("/")) + { + appPath.chop(1); + } + } + auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); + QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png"); + + QFile iconFile(iconPath); + if (!iconFile.open(QFile::WriteOnly)) + { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut.")); + return; + } + bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG"); + iconFile.close(); + + if (!success) + { + iconFile.remove(); + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut.")); + return; + } + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), - QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), icon->getFilePath())) { + appPath, { "--launch", m_selectedInstance->id() }, + m_selectedInstance->name(), iconPath)) { QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); } else { + iconFile.remove(); QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); } #elif defined(Q_OS_WIN) @@ -2137,7 +2172,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut.")); return; } bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO"); @@ -2149,12 +2184,13 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() if (!success) { iconFile.remove(); - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut.")); return; } if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), - QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), iconPath)) { + QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, + m_selectedInstance->name(), iconPath)) { QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); } else From c4cfec1e94d1665dc3c1ccd44966a34f349698ea Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sun, 13 Nov 2022 18:00:18 +0200 Subject: [PATCH 081/277] Undo accidental tomlplusplus downgrade Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- libraries/tomlplusplus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/tomlplusplus b/libraries/tomlplusplus index 4b166b69..cc741c9f 160000 --- a/libraries/tomlplusplus +++ b/libraries/tomlplusplus @@ -1 +1 @@ -Subproject commit 4b166b69f28e70a416a1a04a98f365d2aeb90de8 +Subproject commit cc741c9f5f2a62856a2a2e9e275f61eb0591c09c From acd50969e0fcb172cf5f62e5ced4d10c6a9cbbe6 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sun, 13 Nov 2022 19:50:27 +0200 Subject: [PATCH 082/277] Linux: remove TryExec entry from .desktop files Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index e1059ca9..9d911fa0 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -382,7 +382,6 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri << "\n"; stream << "Type=Application" << "\n"; - stream << "TryExec=\"" << target.toLocal8Bit() << "\"\n"; stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n"; if (!icon.isEmpty()) From 07392d493cf7b1a0cb0e0f11838986935a4d2379 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:52:00 +0100 Subject: [PATCH 083/277] fix: disable building snaps on release the snap infra should already be able to deal with it, i think Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3225cf1e..6104109d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -510,10 +510,10 @@ jobs: echo "VERSION=$ver_short" >> $GITHUB_ENV - name: Package Snap (Linux) id: snapcraft - if: runner.os == 'Linux' && matrix.qt_ver != 5 + if: runner.os == 'Linux' && inputs.build_type == 'Debug' uses: snapcore/action-build@v1 - name: Upload Snap (Linux) - if: runner.os == 'Linux' && matrix.qt_ver != 5 + if: runner.os == 'Linux' && inputs.build_type == 'Debug' uses: actions/upload-artifact@v3 with: name: prismlauncher_${{ env.VERSION }}_amd64.snap From 841c2e9166e01a8ecff61c96a05b3bdef64fe969 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:56:38 +0100 Subject: [PATCH 084/277] fix: disable caching Qt on release it's good practice to not cache on release builds Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 7 +++++-- .github/workflows/trigger_builds.yml | 1 + .github/workflows/trigger_release.yml | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6104109d..f5d98f90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,10 @@ on: description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel) type: string default: Debug + is_qt_cached: + description: Enable Qt caching or not + type: string + default: true secrets: SPARKLE_ED25519_KEY: description: Private key for signing Sparkle updates @@ -200,8 +204,7 @@ jobs: arch: ${{ matrix.qt_arch }} modules: ${{ matrix.qt_modules }} tools: ${{ matrix.qt_tools }} - cache: true - cache-key-prefix: ${{ matrix.qt_host }}-${{ matrix.qt_version }}-"${{ matrix.qt_modules }}"-qt_cache + cache: ${{ inputs.is_qt_cached }} - name: Prepare AppImage (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 8adaa5e5..44751fbc 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -30,5 +30,6 @@ jobs: uses: ./.github/workflows/build.yml with: build_type: Debug + is_qt_cached: true secrets: SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }} diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index d415b2b1..8baa9693 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -12,6 +12,7 @@ jobs: uses: ./.github/workflows/build.yml with: build_type: Release + is_qt_cached: false secrets: SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }} From dcfc15a0a16c3b0d64086621cec8ef8ffc722832 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 9 Nov 2022 20:10:37 +0100 Subject: [PATCH 085/277] feat+fix: move codeql to its own workflow i think it's better practice, it should slightly improve linux qt6 build times and i noticed ccache? is messing up with it because alerts get randomly fixed Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 16 ---------------- .github/workflows/codeql.yml | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5d98f90..61fd9b4c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,14 +106,6 @@ jobs: with: submodules: 'true' - - name: Initialize CodeQL - if: runner.os == 'Linux' && matrix.qt_ver == 6 - uses: github/codeql-action/init@v2 - with: - config-file: ./.github/codeql/codeql-config.yml - queries: security-and-quality - languages: cpp, java - - name: 'Setup MSYS2' if: runner.os == 'Windows' && matrix.msystem != '' uses: msys2/setup-msys2@v2 @@ -295,14 +287,6 @@ jobs: run: | ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }} - ## - # CODE SCAN - ## - - - name: Perform CodeQL Analysis - if: runner.os == 'Linux' && matrix.qt_ver == 6 - uses: github/codeql-action/analyze@v2 - ## # PACKAGE BUILDS ## diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..0cd1f6e4 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,35 @@ +name: "CodeQL Code Scanning" + +on: [ push, pull_request, workflow_dispatch ] + +jobs: + CodeQL: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + config-file: ./.github/codeql/codeql-config.yml + queries: security-and-quality + languages: cpp, java + + - name: Install Dependencies + run: + sudo apt-get -y update + + sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 + + - name: Configure and Build + run: | + cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DLauncher_QT_VERSION_MAJOR=5 -G Ninja + + cmake --build build + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 From 0a04c3a2a77bda4e60d04f651c7ab5006ee02449 Mon Sep 17 00:00:00 2001 From: txtsd Date: Sun, 13 Nov 2022 19:08:37 +0530 Subject: [PATCH 086/277] fix: Fix the error that CodeQL caught Signed-off-by: txtsd --- launcher/minecraft/mod/tasks/LocalModParseTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index a694e7b2..774f6114 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -121,7 +121,7 @@ ModDetails ReadMCModTOML(QByteArray contents) return {}; } auto modsTable = tomlModsTable0->as_table(); - if (!tomlModsTable0) { + if (!modsTable) { qWarning() << "Corrupted mods.toml? [[mods]] was not a table!"; return {}; } From 5be8545edcf53cd410d0ea14168b5675150106fc Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sun, 13 Nov 2022 20:18:51 +0200 Subject: [PATCH 087/277] Windows, Linux: prevent segfault on missing icon Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 4dbac967..4e6ce82c 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2131,7 +2131,11 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); - + if (icon == nullptr) + { + icon = APPLICATION->icons()->icon("grass"); + } + QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png"); QFile iconFile(iconPath); @@ -2162,7 +2166,11 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() } #elif defined(Q_OS_WIN) auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); - + if (icon == nullptr) + { + icon = APPLICATION->icons()->icon("grass"); + } + QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); // part of fix for weird bug involving the window icon being replaced From 97a7af855f8a96a0e73181c5e32a15bbd2cb67f2 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Mon, 14 Nov 2022 19:21:47 +0800 Subject: [PATCH 088/277] slight reword: "install to PATH" Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index c376f4ec..7140831b 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -355,8 +355,8 @@ public: #ifdef Q_OS_MAC actionAddToPATH = TranslatedAction(MainWindow); actionAddToPATH->setObjectName(QStringLiteral("actionAddToPATH")); - actionAddToPATH.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Add to &PATH")); - actionAddToPATH.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Add the prism binary to PATH.")); + actionAddToPATH.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Install to &PATH")); + actionAddToPATH.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Install a prismlauncher symlink to /usr/local/bin")); all_actions.append(&actionAddToPATH); #endif From e14b998da3850e43abf2606064d6b0ddebbf0025 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 14 Nov 2022 18:37:24 +0100 Subject: [PATCH 089/277] refactor: improve readability Signed-off-by: Sefa Eyeoglu --- launcher/ui/MainWindow.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 9375039b..e1ac9551 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1949,16 +1949,24 @@ void MainWindow::on_actionClearMetadata_triggered() } #ifdef Q_OS_MAC -void MainWindow::on_actionAddToPATH_triggered() { +void MainWindow::on_actionAddToPATH_triggered() +{ auto binaryPath = APPLICATION->applicationFilePath(); + auto targetPath = QString("/usr/local/bin/%1").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); + qDebug() << "Symlinking" << binaryPath << "to" << targetPath; - qDebug() << "Symlinking" << binaryPath << "to /usr/local/bin/prism"; - auto outcome = QProcess::execute("/usr/bin/osascript", QStringList()<< "-e" << tr("do shell script \"mkdir -p /usr/local/bin && ln -sf '%1' '/usr/local/bin/prismlauncher'\" with administrator privileges").arg(binaryPath)); - + QStringList args; + args << "-e"; + args << QString("do shell script \"mkdir -p /usr/local/bin && ln -sf '%1' '%2'\" with administrator privileges") + .arg(binaryPath, targetPath); + auto outcome = QProcess::execute("/usr/bin/osascript", args); if (!outcome) { - QMessageBox::information(this, tr("Added Prism to PATH"), tr("Prism was successfully added to your PATH. You can now run it with `prismlauncher` in your Terminal. Enjoy!")); + QMessageBox::information(this, tr("Successfully added %1 to PATH").arg(BuildConfig.LAUNCHER_DISPLAYNAME), + tr("%1 was successfully added to your PATH. You can now start it by running `%2`.") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.LAUNCHER_APP_BINARY_NAME)); } else { - QMessageBox::critical(this, tr("Failed to add Prism to PATH"), tr("Failed to add Prism to PATH :(")); + QMessageBox::critical(this, tr("Failed to add %1 to PATH").arg(BuildConfig.LAUNCHER_DISPLAYNAME), + tr("An error occurred while trying to add %1 to PATH").arg(BuildConfig.LAUNCHER_DISPLAYNAME)); } } #endif From ce892c9e777f13de6e6298806d9fdb5c92f77af6 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Mon, 14 Nov 2022 21:02:31 +0200 Subject: [PATCH 090/277] Add icon NOTE: Currently missing on Legacy, Flat and Flat (White) Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/resources/OSX/OSX.qrc | 4 +- launcher/resources/OSX/scalable/shortcut.svg | 14 +++++++ launcher/resources/iOS/iOS.qrc | 4 +- launcher/resources/iOS/scalable/shortcut.svg | 13 ++++++ launcher/resources/pe_blue/pe_blue.qrc | 4 +- .../resources/pe_blue/scalable/shortcut.svg | 41 +++++++++++++++++++ launcher/resources/pe_colored/pe_colored.qrc | 4 +- .../pe_colored/scalable/shortcut.svg | 13 ++++++ launcher/resources/pe_dark/pe_dark.qrc | 4 +- .../resources/pe_dark/scalable/shortcut.svg | 41 +++++++++++++++++++ launcher/resources/pe_light/pe_light.qrc | 4 +- .../resources/pe_light/scalable/shortcut.svg | 41 +++++++++++++++++++ launcher/ui/MainWindow.cpp | 3 +- 13 files changed, 177 insertions(+), 13 deletions(-) create mode 100644 launcher/resources/OSX/scalable/shortcut.svg create mode 100644 launcher/resources/iOS/scalable/shortcut.svg create mode 100644 launcher/resources/pe_blue/scalable/shortcut.svg create mode 100644 launcher/resources/pe_colored/scalable/shortcut.svg create mode 100644 launcher/resources/pe_dark/scalable/shortcut.svg create mode 100644 launcher/resources/pe_light/scalable/shortcut.svg diff --git a/launcher/resources/OSX/OSX.qrc b/launcher/resources/OSX/OSX.qrc index 19fe312b..3f50d6cf 100644 --- a/launcher/resources/OSX/OSX.qrc +++ b/launcher/resources/OSX/OSX.qrc @@ -1,5 +1,4 @@ - - + index.theme scalable/about.svg @@ -39,5 +38,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/shortcut.svg diff --git a/launcher/resources/OSX/scalable/shortcut.svg b/launcher/resources/OSX/scalable/shortcut.svg new file mode 100644 index 00000000..a2b7488e --- /dev/null +++ b/launcher/resources/OSX/scalable/shortcut.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/launcher/resources/iOS/iOS.qrc b/launcher/resources/iOS/iOS.qrc index aa08d811..d7044fbb 100644 --- a/launcher/resources/iOS/iOS.qrc +++ b/launcher/resources/iOS/iOS.qrc @@ -1,5 +1,4 @@ - - + index.theme scalable/about.svg @@ -39,5 +38,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/shortcut.svg diff --git a/launcher/resources/iOS/scalable/shortcut.svg b/launcher/resources/iOS/scalable/shortcut.svg new file mode 100644 index 00000000..16e9fa48 --- /dev/null +++ b/launcher/resources/iOS/scalable/shortcut.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/launcher/resources/pe_blue/pe_blue.qrc b/launcher/resources/pe_blue/pe_blue.qrc index 3121ffe6..dc40103a 100644 --- a/launcher/resources/pe_blue/pe_blue.qrc +++ b/launcher/resources/pe_blue/pe_blue.qrc @@ -1,5 +1,4 @@ - - + index.theme scalable/about.svg @@ -39,5 +38,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/shortcut.svg diff --git a/launcher/resources/pe_blue/scalable/shortcut.svg b/launcher/resources/pe_blue/scalable/shortcut.svg new file mode 100644 index 00000000..45b73496 --- /dev/null +++ b/launcher/resources/pe_blue/scalable/shortcut.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/pe_colored/pe_colored.qrc b/launcher/resources/pe_colored/pe_colored.qrc index ce5ad8e2..bd1af6ff 100644 --- a/launcher/resources/pe_colored/pe_colored.qrc +++ b/launcher/resources/pe_colored/pe_colored.qrc @@ -1,5 +1,4 @@ - - + index.theme scalable/about.svg @@ -39,5 +38,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/shortcut.svg diff --git a/launcher/resources/pe_colored/scalable/shortcut.svg b/launcher/resources/pe_colored/scalable/shortcut.svg new file mode 100644 index 00000000..1469674f --- /dev/null +++ b/launcher/resources/pe_colored/scalable/shortcut.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/launcher/resources/pe_dark/pe_dark.qrc b/launcher/resources/pe_dark/pe_dark.qrc index 156d8f8b..05ef7e93 100644 --- a/launcher/resources/pe_dark/pe_dark.qrc +++ b/launcher/resources/pe_dark/pe_dark.qrc @@ -1,5 +1,4 @@ - - + index.theme scalable/about.svg @@ -39,5 +38,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/shortcut.svg diff --git a/launcher/resources/pe_dark/scalable/shortcut.svg b/launcher/resources/pe_dark/scalable/shortcut.svg new file mode 100644 index 00000000..29b45f26 --- /dev/null +++ b/launcher/resources/pe_dark/scalable/shortcut.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/pe_light/pe_light.qrc b/launcher/resources/pe_light/pe_light.qrc index d8e4a1bd..6acca230 100644 --- a/launcher/resources/pe_light/pe_light.qrc +++ b/launcher/resources/pe_light/pe_light.qrc @@ -1,5 +1,4 @@ - - + index.theme scalable/about.svg @@ -39,5 +38,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/shortcut.svg diff --git a/launcher/resources/pe_light/scalable/shortcut.svg b/launcher/resources/pe_light/scalable/shortcut.svg new file mode 100644 index 00000000..4d232bcf --- /dev/null +++ b/launcher/resources/pe_light/scalable/shortcut.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 4e6ce82c..0c606660 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -743,7 +743,8 @@ public: actionCreateInstanceShortcut.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Create Shortcut")); actionCreateInstanceShortcut.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Creates a shortcut on your desktop to launch the selected instance.")); //actionCreateInstanceShortcut->setShortcut(QKeySequence(tr("Ctrl+D"))); // TODO - //actionCreateInstanceShortcut->setIcon(APPLICATION->getThemedIcon("copy")); // TODO + // FIXME missing on Legacy, Flat and Flat (White) + actionCreateInstanceShortcut->setIcon(APPLICATION->getThemedIcon("shortcut")); all_actions.append(&actionCreateInstanceShortcut); setInstanceActionsEnabled(false); From 472d931b4b5cedcf570153c4ba40cddbc611040a Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Wed, 9 Nov 2022 20:57:54 +0000 Subject: [PATCH 091/277] Prefix member variables in HttpMetaCache MSVC warns about shadowing variables Signed-off-by: TheLastRar --- launcher/net/HttpMetaCache.cpp | 72 +++++++++++++++++----------------- launcher/net/HttpMetaCache.h | 54 ++++++++++++------------- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index e242dcf4..0d7ca769 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -47,7 +47,7 @@ auto MetaEntry::getFullPath() -> QString { // FIXME: make local? - return FS::PathCombine(basePath, relativePath); + return FS::PathCombine(m_basePath, m_relativePath); } HttpMetaCache::HttpMetaCache(QString path) : QObject(), m_index_file(path) @@ -99,7 +99,7 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex return staleEntry(base, resource_path); } - if (!expected_etag.isEmpty() && expected_etag != entry->etag) { + if (!expected_etag.isEmpty() && expected_etag != entry->m_etag) { // if the etag doesn't match expected, we disown the entry selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); @@ -107,17 +107,17 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex // if the file changed, check md5sum qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch(); - if (file_last_changed != entry->local_changed_timestamp) { + if (file_last_changed != entry->m_local_changed_timestamp) { QFile input(real_path); input.open(QIODevice::ReadOnly); QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5).toHex().constData(); - if (entry->md5sum != md5sum) { + if (entry->m_md5sum != md5sum) { selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } // md5sums matched... keep entry and save the new state to file - entry->local_changed_timestamp = file_last_changed; + entry->m_local_changed_timestamp = file_last_changed; SaveEventually(); } @@ -130,23 +130,23 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex } // entry passed all the checks we cared about. - entry->basePath = getBasePath(base); + entry->m_basePath = getBasePath(base); return entry; } auto HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) -> bool { - if (!m_entries.contains(stale_entry->baseId)) { - qCritical() << "Cannot add entry with unknown base: " << stale_entry->baseId.toLocal8Bit(); + if (!m_entries.contains(stale_entry->m_baseId)) { + qCritical() << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit(); return false; } - if (stale_entry->stale) { + if (stale_entry->m_stale) { qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); return false; } - m_entries[stale_entry->baseId].entry_list[stale_entry->relativePath] = stale_entry; + m_entries[stale_entry->m_baseId].entry_list[stale_entry->m_relativePath] = stale_entry; SaveEventually(); return true; @@ -157,7 +157,7 @@ auto HttpMetaCache::evictEntry(MetaEntryPtr entry) -> bool if (!entry) return false; - entry->stale = true; + entry->m_stale = true; SaveEventually(); return true; } @@ -169,7 +169,7 @@ void HttpMetaCache::evictAll() qDebug() << "Evicting base" << base; for (MetaEntryPtr entry : map.entry_list) { if (!evictEntry(entry)) - qWarning() << "Unexpected missing cache entry" << entry->basePath; + qWarning() << "Unexpected missing cache entry" << entry->m_basePath; } } } @@ -177,10 +177,10 @@ void HttpMetaCache::evictAll() auto HttpMetaCache::staleEntry(QString base, QString resource_path) -> MetaEntryPtr { auto foo = new MetaEntry(); - foo->baseId = base; - foo->basePath = getBasePath(base); - foo->relativePath = resource_path; - foo->stale = true; + foo->m_baseId = base; + foo->m_basePath = getBasePath(base); + foo->m_relativePath = resource_path; + foo->m_stale = true; return MetaEntryPtr(foo); } @@ -235,23 +235,23 @@ void HttpMetaCache::Load() auto& entrymap = m_entries[base]; auto foo = new MetaEntry(); - foo->baseId = base; - foo->relativePath = Json::ensureString(element_obj, "path"); - foo->md5sum = Json::ensureString(element_obj, "md5sum"); - foo->etag = Json::ensureString(element_obj, "etag"); - foo->local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp"); - foo->remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp"); + foo->m_baseId = base; + foo->m_relativePath = Json::ensureString(element_obj, "path"); + foo->m_md5sum = Json::ensureString(element_obj, "md5sum"); + foo->m_etag = Json::ensureString(element_obj, "etag"); + foo->m_local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp"); + foo->m_remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp"); foo->makeEternal(Json::ensureBoolean(element_obj, (const QString)QStringLiteral("eternal"), false)); if (!foo->isEternal()) { - foo->current_age = Json::ensureDouble(element_obj, "current_age"); - foo->max_age = Json::ensureDouble(element_obj, "max_age"); + foo->m_current_age = Json::ensureDouble(element_obj, "current_age"); + foo->m_max_age = Json::ensureDouble(element_obj, "max_age"); } // presumed innocent until closer examination - foo->stale = false; + foo->m_stale = false; - entrymap.entry_list[foo->relativePath] = MetaEntryPtr(foo); + entrymap.entry_list[foo->m_relativePath] = MetaEntryPtr(foo); } } @@ -276,23 +276,23 @@ void HttpMetaCache::SaveNow() for (auto group : m_entries) { for (auto entry : group.entry_list) { // do not save stale entries. they are dead. - if (entry->stale) { + if (entry->m_stale) { continue; } QJsonObject entryObj; - Json::writeString(entryObj, "base", entry->baseId); - Json::writeString(entryObj, "path", entry->relativePath); - Json::writeString(entryObj, "md5sum", entry->md5sum); - Json::writeString(entryObj, "etag", entry->etag); - entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->local_changed_timestamp))); - if (!entry->remote_changed_timestamp.isEmpty()) - entryObj.insert("remote_changed_timestamp", QJsonValue(entry->remote_changed_timestamp)); + Json::writeString(entryObj, "base", entry->m_baseId); + Json::writeString(entryObj, "path", entry->m_relativePath); + Json::writeString(entryObj, "md5sum", entry->m_md5sum); + Json::writeString(entryObj, "etag", entry->m_etag); + entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->m_local_changed_timestamp))); + if (!entry->m_remote_changed_timestamp.isEmpty()) + entryObj.insert("remote_changed_timestamp", QJsonValue(entry->m_remote_changed_timestamp)); if (entry->isEternal()) { entryObj.insert("eternal", true); } else { - entryObj.insert("current_age", QJsonValue(double(entry->current_age))); - entryObj.insert("max_age", QJsonValue(double(entry->max_age))); + entryObj.insert("current_age", QJsonValue(double(entry->m_current_age))); + entryObj.insert("max_age", QJsonValue(double(entry->m_max_age))); } entriesArr.append(entryObj); } diff --git a/launcher/net/HttpMetaCache.h b/launcher/net/HttpMetaCache.h index 2a07d65a..37f4b49a 100644 --- a/launcher/net/HttpMetaCache.h +++ b/launcher/net/HttpMetaCache.h @@ -49,47 +49,47 @@ class MetaEntry { MetaEntry() = default; public: - auto isStale() -> bool { return stale; } - void setStale(bool stale) { this->stale = stale; } + auto isStale() -> bool { return m_stale; } + void setStale(bool stale) { m_stale = stale; } auto getFullPath() -> QString; - auto getRemoteChangedTimestamp() -> QString { return remote_changed_timestamp; } - void setRemoteChangedTimestamp(QString remote_changed_timestamp) { this->remote_changed_timestamp = remote_changed_timestamp; } - void setLocalChangedTimestamp(qint64 timestamp) { local_changed_timestamp = timestamp; } + auto getRemoteChangedTimestamp() -> QString { return m_remote_changed_timestamp; } + void setRemoteChangedTimestamp(QString remote_changed_timestamp) { m_remote_changed_timestamp = remote_changed_timestamp; } + void setLocalChangedTimestamp(qint64 timestamp) { m_local_changed_timestamp = timestamp; } - auto getETag() -> QString { return etag; } - void setETag(QString etag) { this->etag = etag; } + auto getETag() -> QString { return m_etag; } + void setETag(QString etag) { m_etag = etag; } - auto getMD5Sum() -> QString { return md5sum; } - void setMD5Sum(QString md5sum) { this->md5sum = md5sum; } + auto getMD5Sum() -> QString { return m_md5sum; } + void setMD5Sum(QString md5sum) { m_md5sum = md5sum; } /* Whether the entry expires after some time (false) or not (true). */ - void makeEternal(bool eternal) { is_eternal = eternal; } - [[nodiscard]] bool isEternal() const { return is_eternal; } + void makeEternal(bool eternal) { m_is_eternal = eternal; } + [[nodiscard]] bool isEternal() const { return m_is_eternal; } - auto getCurrentAge() -> qint64 { return current_age; } - void setCurrentAge(qint64 age) { current_age = age; } + auto getCurrentAge() -> qint64 { return m_current_age; } + void setCurrentAge(qint64 age) { m_current_age = age; } - auto getMaximumAge() -> qint64 { return max_age; } - void setMaximumAge(qint64 age) { max_age = age; } + auto getMaximumAge() -> qint64 { return m_max_age; } + void setMaximumAge(qint64 age) { m_max_age = age; } - bool isExpired(qint64 offset) { return !is_eternal && (current_age >= max_age - offset); }; + bool isExpired(qint64 offset) { return !m_is_eternal && (m_current_age >= m_max_age - offset); }; protected: - QString baseId; - QString basePath; - QString relativePath; - QString md5sum; - QString etag; + QString m_baseId; + QString m_basePath; + QString m_relativePath; + QString m_md5sum; + QString m_etag; - qint64 local_changed_timestamp = 0; - QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time - qint64 current_age = 0; - qint64 max_age = 0; - bool is_eternal = false; + qint64 m_local_changed_timestamp = 0; + QString m_remote_changed_timestamp; // QString for now, RFC 2822 encoded time + qint64 m_current_age = 0; + qint64 m_max_age = 0; + bool m_is_eternal = false; - bool stale = true; + bool m_stale = true; }; using MetaEntryPtr = std::shared_ptr; From 55c7b291e1dc5ef903dfbbea18d3f48fd968df38 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Wed, 9 Nov 2022 21:11:34 +0000 Subject: [PATCH 092/277] Remove unused qhash function Signed-off-by: TheLastRar --- launcher/meta/JsonFormat.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/launcher/meta/JsonFormat.h b/launcher/meta/JsonFormat.h index 93217b7e..63128a4e 100644 --- a/launcher/meta/JsonFormat.h +++ b/launcher/meta/JsonFormat.h @@ -60,11 +60,6 @@ struct Require QString suggests; }; -inline Q_DECL_PURE_FUNCTION uint qHash(const Require &key, uint seed = 0) Q_DECL_NOTHROW -{ - return qHash(key.uid, seed); -} - using RequireSet = std::set; void parseIndex(const QJsonObject &obj, Index *ptr); From b8d7aedb2ca38a64e84aad4955044c778ca4163d Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Wed, 9 Nov 2022 21:15:35 +0000 Subject: [PATCH 093/277] Mark paramater line as unused in guessLevel() The base method doesn't use this variable, but classes overriding this method do Signed-off-by: TheLastRar --- launcher/BaseInstance.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 307240e0..a2a4f824 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -151,7 +151,7 @@ public: void copyManagedPack(BaseInstance& other); /// guess log level from a line of game log - virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) + virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level) { return level; }; From 3cba359d38a5587506a1535c06ef7e8399e079d5 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Wed, 9 Nov 2022 21:18:34 +0000 Subject: [PATCH 094/277] Mark paramater runtimeContext as unused Base class uses variable, but ImplicitRule does not Signed-off-by: TheLastRar --- launcher/minecraft/Rule.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/Rule.h b/launcher/minecraft/Rule.h index 236f9a87..846e8e42 100644 --- a/launcher/minecraft/Rule.h +++ b/launcher/minecraft/Rule.h @@ -104,7 +104,7 @@ public: class ImplicitRule : public Rule { protected: - virtual bool applies(const Library *, const RuntimeContext & runtimeContext) + virtual bool applies(const Library *, [[maybe_unused]] const RuntimeContext & runtimeContext) { return true; } From f8a137a26e2fbff287b2d15d897f1dea88df4c38 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Sun, 13 Nov 2022 00:52:24 +0000 Subject: [PATCH 095/277] Mark loadExtraPackInfo abstract All classes that inherit from ModModel override this method Signed-off-by: TheLastRar --- launcher/ui/pages/modplatform/ModModel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index d2636d87..5bc31cc3 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -41,7 +41,7 @@ class ListModel : public QAbstractListModel { void requestModVersions(const ModPlatform::IndexedPack& current, QModelIndex index); virtual void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; - virtual void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) {}; + virtual void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) = 0; virtual void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) = 0; void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); From 08f8623cb700cc51d6953b573d432f4553a5c736 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Sun, 13 Nov 2022 00:54:37 +0000 Subject: [PATCH 096/277] Mark paramaters of onParseFailed as unused Signed-off-by: TheLastRar --- launcher/minecraft/mod/ResourceFolderModel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 25095a45..846df4d0 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -176,7 +176,7 @@ class ResourceFolderModel : public QAbstractListModel { * if the resource is complex and has more stuff to parse. */ virtual void onParseSucceeded(int ticket, QString resource_id); - virtual void onParseFailed(int ticket, QString resource_id) {} + virtual void onParseFailed([[maybe_unused]] int ticket, [[maybe_unused]] QString resource_id) {} protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. From 5558f3d2ccde1f1a043fbb82c0856ae3241dc20f Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Sun, 13 Nov 2022 14:33:25 +0000 Subject: [PATCH 097/277] Mark paramaters of dropMimeData as unused Signed-off-by: TheLastRar --- launcher/icons/IconList.cpp | 2 +- launcher/minecraft/WorldList.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index 3a223d1b..a1f77cc3 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -242,7 +242,7 @@ Qt::DropActions IconList::supportedDropActions() const return Qt::CopyAction; } -bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex &parent) { if (action == Qt::IgnoreAction) return true; diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index aee7be35..3634d144 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -398,8 +398,8 @@ void WorldList::installWorld(QFileInfo filename) w.install(m_dir.absolutePath()); } -bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent) +bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex &parent) { if (action == Qt::IgnoreAction) return true; From fce323b945d5e8365c64c61e3ba592cb2b7446c4 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Sun, 13 Nov 2022 14:35:55 +0000 Subject: [PATCH 098/277] Check parent in rowCount/columnCount/canFetchMore Signed-off-by: TheLastRar --- launcher/BaseVersionList.cpp | 4 ++-- launcher/VersionProxyModel.cpp | 4 ++-- launcher/icons/IconList.cpp | 2 +- launcher/meta/Index.cpp | 4 ++-- launcher/minecraft/PackProfile.cpp | 4 ++-- launcher/minecraft/WorldList.cpp | 2 +- launcher/minecraft/WorldList.h | 2 +- launcher/minecraft/auth/AccountList.cpp | 8 ++++---- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourceFolderModel.h | 4 ++-- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- launcher/ui/pages/instance/ServersPage.cpp | 4 ++-- launcher/ui/pages/modplatform/ModModel.h | 6 +++--- launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp | 4 ++-- .../pages/modplatform/atlauncher/AtlOptionalModDialog.cpp | 4 ++-- launcher/ui/pages/modplatform/flame/FlameModel.cpp | 4 ++-- launcher/ui/pages/modplatform/ftb/FtbListModel.cpp | 4 ++-- launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp | 4 ++-- launcher/ui/pages/modplatform/modrinth/ModrinthModel.h | 6 +++--- launcher/ui/pages/modplatform/technic/TechnicModel.cpp | 8 ++++---- 20 files changed, 41 insertions(+), 41 deletions(-) diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index 4ed82612..dc95e7ea 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -95,12 +95,12 @@ BaseVersionList::RoleList BaseVersionList::providesRoles() const int BaseVersionList::rowCount(const QModelIndex &parent) const { // Return count - return count(); + return parent.isValid() ? 0 : count(); } int BaseVersionList::columnCount(const QModelIndex &parent) const { - return 1; + return parent.isValid() ? 0 : 1; } QHash BaseVersionList::roleNames() const diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 032f21f9..6aba268d 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -311,14 +311,14 @@ QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex &par int VersionProxyModel::columnCount(const QModelIndex &parent) const { - return m_columns.size(); + return parent.isValid() ? 0 : m_columns.size(); } int VersionProxyModel::rowCount(const QModelIndex &parent) const { if(sourceModel()) { - return sourceModel()->rowCount(); + return sourceModel()->rowCount(parent); } return 0; } diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index a1f77cc3..01043ad2 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -302,7 +302,7 @@ QVariant IconList::data(const QModelIndex &index, int role) const int IconList::rowCount(const QModelIndex &parent) const { - return icons.size(); + return parent.isValid() ? 0 : icons.size(); } void IconList::installIcons(const QStringList &iconFiles) diff --git a/launcher/meta/Index.cpp b/launcher/meta/Index.cpp index eec1b329..242aad9f 100644 --- a/launcher/meta/Index.cpp +++ b/launcher/meta/Index.cpp @@ -58,11 +58,11 @@ QVariant Index::data(const QModelIndex &index, int role) const } int Index::rowCount(const QModelIndex &parent) const { - return m_lists.size(); + return parent.isValid() ? 0 : m_lists.size(); } int Index::columnCount(const QModelIndex &parent) const { - return 1; + return parent.isValid() ? 0 : 1; } QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const { diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 1618458f..89b85d6a 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -675,12 +675,12 @@ Qt::ItemFlags PackProfile::flags(const QModelIndex &index) const int PackProfile::rowCount(const QModelIndex &parent) const { - return d->components.size(); + return parent.isValid() ? 0 : d->components.size(); } int PackProfile::columnCount(const QModelIndex &parent) const { - return NUM_COLUMNS; + return parent.isValid() ? 0 : NUM_COLUMNS; } void PackProfile::move(const int index, const MoveDirection direction) diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp index 3634d144..ae29a972 100644 --- a/launcher/minecraft/WorldList.cpp +++ b/launcher/minecraft/WorldList.cpp @@ -173,7 +173,7 @@ bool WorldList::resetIcon(int row) int WorldList::columnCount(const QModelIndex &parent) const { - return 4; + return parent.isValid()? 0 : 4; } QVariant WorldList::data(const QModelIndex &index, int role) const diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h index 5138e583..08294755 100644 --- a/launcher/minecraft/WorldList.h +++ b/launcher/minecraft/WorldList.h @@ -54,7 +54,7 @@ public: virtual int rowCount(const QModelIndex &parent = QModelIndex()) const { - return size(); + return parent.isValid() ? 0 : static_cast(size()); }; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index b3b57c74..6ea62a50 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -408,15 +408,15 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r } } -int AccountList::rowCount(const QModelIndex &) const +int AccountList::rowCount(const QModelIndex &parent) const { // Return count - return count(); + return parent.isValid() ? 0 : count(); } -int AccountList::columnCount(const QModelIndex &) const +int AccountList::columnCount(const QModelIndex &parent) const { - return NUM_COLUMNS; + return parent.isValid() ? 0 : NUM_COLUMNS; } Qt::ItemFlags AccountList::flags(const QModelIndex &index) const diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 66e80f4a..4ccc5d4d 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -144,7 +144,7 @@ QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, in int ModFolderModel::columnCount(const QModelIndex &parent) const { - return NUM_COLUMNS; + return parent.isValid() ? 0 : NUM_COLUMNS; } Task* ModFolderModel::createUpdateTask() diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 846df4d0..74a1b62d 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -90,8 +90,8 @@ class ResourceFolderModel : public QAbstractListModel { /* Basic columns */ enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS }; - [[nodiscard]] int rowCount(const QModelIndex& = {}) const override { return size(); } - [[nodiscard]] int columnCount(const QModelIndex& = {}) const override { return NUM_COLUMNS; }; + [[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast(size()); } + [[nodiscard]] int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; }; [[nodiscard]] Qt::DropActions supportedDropActions() const override; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index f8a6c1cf..ebac707d 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -137,7 +137,7 @@ QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orient int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const { - return NUM_COLUMNS; + return parent.isValid() ? 0 : NUM_COLUMNS; } Task* ResourcePackFolderModel::createUpdateTask() diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index 5e8bd7cc..d64bcb76 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -400,11 +400,11 @@ public: virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override { - return m_servers.size(); + return parent.isValid() ? 0 : m_servers.size(); } int columnCount(const QModelIndex & parent) const override { - return COLUMN_COUNT; + return parent.isValid() ? 0 : COLUMN_COUNT; } Server * at(int index) diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 5bc31cc3..36840649 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -20,8 +20,8 @@ class ListModel : public QAbstractListModel { ListModel(ModPage* parent); ~ListModel() override; - inline auto rowCount(const QModelIndex& parent) const -> int override { return modpacks.size(); }; - inline auto columnCount(const QModelIndex& parent) const -> int override { return 1; }; + inline auto rowCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : modpacks.size(); }; + inline auto columnCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : 1; }; inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }; auto debugName() const -> QString; @@ -46,7 +46,7 @@ class ListModel : public QAbstractListModel { void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); - inline auto canFetchMore(const QModelIndex& parent) const -> bool override { return searchState == CanPossiblyFetchMore; }; + inline auto canFetchMore(const QModelIndex& parent) const -> bool override { return parent.isValid() ? false : searchState == CanPossiblyFetchMore; }; public slots: void searchRequestFinished(QJsonDocument& doc); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index ef9a9268..2ce04068 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -32,12 +32,12 @@ ListModel::~ListModel() int ListModel::rowCount(const QModelIndex &parent) const { - return modpacks.size(); + return parent.isValid() ? 0 : modpacks.size(); } int ListModel::columnCount(const QModelIndex &parent) const { - return 1; + return parent.isValid() ? 0 : 1; } QVariant ListModel::data(const QModelIndex &index, int role) const diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index 9138dcbb..cdb4532c 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -75,12 +75,12 @@ QVector AtlOptionalModListModel::getResult() { } int AtlOptionalModListModel::rowCount(const QModelIndex &parent) const { - return m_mods.size(); + return parent.isValid() ? 0 : m_mods.size(); } int AtlOptionalModListModel::columnCount(const QModelIndex &parent) const { // Enabled, Name, Description - return 3; + return parent.isValid() ? 0 : 3; } QVariant AtlOptionalModListModel::data(const QModelIndex &index, int role) const { diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index debae8c3..127c3de5 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -15,12 +15,12 @@ ListModel::~ListModel() {} int ListModel::rowCount(const QModelIndex& parent) const { - return modpacks.size(); + return parent.isValid() ? 0 : modpacks.size(); } int ListModel::columnCount(const QModelIndex& parent) const { - return 1; + return parent.isValid() ? 0 : 1; } QVariant ListModel::data(const QModelIndex& index, int role) const diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp index 3a149944..ce2b2b18 100644 --- a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp +++ b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp @@ -34,12 +34,12 @@ ListModel::~ListModel() int ListModel::rowCount(const QModelIndex &parent) const { - return modpacks.size(); + return parent.isValid() ? 0 : modpacks.size(); } int ListModel::columnCount(const QModelIndex &parent) const { - return 1; + return parent.isValid() ? 0 : 1; } QVariant ListModel::data(const QModelIndex &index, int role) const diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 6f11cc95..6b1f6b89 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -125,12 +125,12 @@ QString ListModel::translatePackType(PackType type) const int ListModel::rowCount(const QModelIndex &parent) const { - return modpacks.size(); + return parent.isValid() ? 0 : modpacks.size(); } int ListModel::columnCount(const QModelIndex &parent) const { - return 1; + return parent.isValid() ? 0 : 1; } QVariant ListModel::data(const QModelIndex &index, int role) const diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index 6f33e11e..3be377a1 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -55,8 +55,8 @@ class ModpackListModel : public QAbstractListModel { ModpackListModel(ModrinthPage* parent); ~ModpackListModel() override = default; - inline auto rowCount(const QModelIndex& parent) const -> int override { return modpacks.size(); }; - inline auto columnCount(const QModelIndex& parent) const -> int override { return 1; }; + inline auto rowCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : modpacks.size(); }; + inline auto columnCount(const QModelIndex& parent) const -> int override { return parent.isValid() ? 0 : 1; }; inline auto flags(const QModelIndex& index) const -> Qt::ItemFlags override { return QAbstractListModel::flags(index); }; auto debugName() const -> QString; @@ -74,7 +74,7 @@ class ModpackListModel : public QAbstractListModel { void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback); - inline auto canFetchMore(const QModelIndex& parent) const -> bool override { return searchState == CanPossiblyFetchMore; }; + inline auto canFetchMore(const QModelIndex& parent) const -> bool override { return parent.isValid() ? false : searchState == CanPossiblyFetchMore; }; public slots: void searchRequestFinished(QJsonDocument& doc_all); diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index 742f4f2a..b2af1ac0 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -80,14 +80,14 @@ QVariant Technic::ListModel::data(const QModelIndex& index, int role) const return QVariant(); } -int Technic::ListModel::columnCount(const QModelIndex&) const +int Technic::ListModel::columnCount(const QModelIndex& parent) const { - return 1; + return parent.isValid() ? 0 : 1; } -int Technic::ListModel::rowCount(const QModelIndex&) const +int Technic::ListModel::rowCount(const QModelIndex& parent) const { - return modpacks.size(); + return parent.isValid() ? 0 : modpacks.size(); } void Technic::ListModel::searchWithTerm(const QString& term) From ac993aa31fcab79473f62c2d958b91e921b6930e Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Sun, 13 Nov 2022 02:10:45 +0000 Subject: [PATCH 099/277] Use Q_UNUSED instead of [[maybe_unused]] in onParseFailed Qt5 on Ubuntu cannot handle [[maybe_unused]] in this function Signed-off-by: TheLastRar --- launcher/minecraft/mod/ResourceFolderModel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 74a1b62d..fe283b04 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -176,7 +176,7 @@ class ResourceFolderModel : public QAbstractListModel { * if the resource is complex and has more stuff to parse. */ virtual void onParseSucceeded(int ticket, QString resource_id); - virtual void onParseFailed([[maybe_unused]] int ticket, [[maybe_unused]] QString resource_id) {} + virtual void onParseFailed(int ticket, QString resource_id) { Q_UNUSED(ticket); Q_UNUSED(resource_id); } protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. From aef5349aee82922fb7cedab7489bc84766453700 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Mon, 14 Nov 2022 19:04:41 +0000 Subject: [PATCH 100/277] Pass index.parent() as parent parameter for rowCount Signed-off-by: TheLastRar --- launcher/minecraft/PackProfile.cpp | 2 +- launcher/minecraft/auth/AccountList.cpp | 2 +- launcher/minecraft/mod/ResourceFolderModel.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 89b85d6a..6ce525eb 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -613,7 +613,7 @@ QVariant PackProfile::data(const QModelIndex &index, int role) const bool PackProfile::setData(const QModelIndex& index, const QVariant& value, int role) { - if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index)) + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) { return false; } diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index 6ea62a50..9e2fd111 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -421,7 +421,7 @@ int AccountList::columnCount(const QModelIndex &parent) const Qt::ItemFlags AccountList::flags(const QModelIndex &index) const { - if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) + if (index.row() < 0 || index.row() >= rowCount(index.parent()) || !index.isValid()) { return Qt::NoItemFlags; } diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index b2356309..0310c8f6 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -426,7 +426,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& value, int role) { int row = index.row(); - if (row < 0 || row >= rowCount(index) || !index.isValid()) + if (row < 0 || row >= rowCount(index.parent()) || !index.isValid()) return false; if (role == Qt::CheckStateRole) From 8d6aecd9447f0740d70f7397a176bb744605c2e3 Mon Sep 17 00:00:00 2001 From: leo78913 Date: Sun, 6 Nov 2022 12:25:11 -0300 Subject: [PATCH 101/277] add the missing icons to the legacy icon theme Signed-off-by: leo78913 --- launcher/resources/multimc/multimc.qrc | 8 + .../resources/multimc/scalable/delete.svg | 282 +++++++++++ .../resources/multimc/scalable/export.svg | 466 ++++++++++++++++++ .../resources/multimc/scalable/launch.svg | 96 ++++ .../resources/multimc/scalable/rename.svg | 437 ++++++++++++++++ launcher/resources/multimc/scalable/tag.svg | 398 +++++++++++++++ 6 files changed, 1687 insertions(+) create mode 100644 launcher/resources/multimc/scalable/delete.svg create mode 100644 launcher/resources/multimc/scalable/export.svg create mode 100644 launcher/resources/multimc/scalable/launch.svg create mode 100644 launcher/resources/multimc/scalable/rename.svg create mode 100644 launcher/resources/multimc/scalable/tag.svg diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 3f3d22fc..270dd009 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -312,5 +312,13 @@ scalable/instances/fox.svg scalable/instances/bee.svg scalable/instances/prismlauncher.svg + + + scalable/delete.svg + scalable/tag.svg + scalable/rename.svg + + scalable/export.svg + scalable/launch.svg diff --git a/launcher/resources/multimc/scalable/delete.svg b/launcher/resources/multimc/scalable/delete.svg new file mode 100644 index 00000000..414cbd5c --- /dev/null +++ b/launcher/resources/multimc/scalable/delete.svg @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/scalable/export.svg b/launcher/resources/multimc/scalable/export.svg new file mode 100644 index 00000000..2605de14 --- /dev/null +++ b/launcher/resources/multimc/scalable/export.svg @@ -0,0 +1,466 @@ + + + + + + + + + + + unsorted + + + + + Open Clip Art Library, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons + + + + + + + + + + + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/scalable/launch.svg b/launcher/resources/multimc/scalable/launch.svg new file mode 100644 index 00000000..321647a0 --- /dev/null +++ b/launcher/resources/multimc/scalable/launch.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/scalable/rename.svg b/launcher/resources/multimc/scalable/rename.svg new file mode 100644 index 00000000..a585e264 --- /dev/null +++ b/launcher/resources/multimc/scalable/rename.svg @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/scalable/tag.svg b/launcher/resources/multimc/scalable/tag.svg new file mode 100644 index 00000000..81b5d545 --- /dev/null +++ b/launcher/resources/multimc/scalable/tag.svg @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From be765f8d883697d2284343f9ee6c858987622857 Mon Sep 17 00:00:00 2001 From: Redson Date: Mon, 14 Nov 2022 16:33:25 -0300 Subject: [PATCH 102/277] feat: Add empty menu Signed-off-by: Redson --- launcher/ui/MainWindow.cpp | 26 ++++++++++++++++++++++++++ launcher/ui/MainWindow.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 85b00b67..31d0f6c2 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -264,6 +264,8 @@ public: TranslatedAction actionLockToolbars; + TranslatedAction actionChangeTheme; + QVector all_toolbuttons; QWidget *centralWidget = nullptr; @@ -428,6 +430,11 @@ public: actionLockToolbars.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Lock Toolbars")); actionLockToolbars->setCheckable(true); all_actions.append(&actionLockToolbars); + + actionChangeTheme = TranslatedAction(MainWindow); + actionChangeTheme->setObjectName(QStringLiteral("actionChangeTheme")); + actionChangeTheme.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Themes")); + all_actions.append(&actionChangeTheme); } void createMainToolbar(QMainWindow *MainWindow) @@ -528,6 +535,8 @@ public: viewMenu = menuBar->addMenu(tr("&View")); viewMenu->setSeparatorsCollapsible(false); + viewMenu->addAction(actionChangeTheme); + viewMenu->addSeparator(); viewMenu->addAction(actionCAT); viewMenu->addSeparator(); @@ -822,6 +831,7 @@ public: createInstanceToolbar(MainWindow); MainWindow->updateToolsMenu(); + MainWindow->updateThemeMenu(); retranslateUi(MainWindow); @@ -1271,6 +1281,22 @@ void MainWindow::updateToolsMenu() ui->actionLaunchInstance->setMenu(launchMenu); } +void MainWindow::updateThemeMenu() +{ + QMenu *themeMenu = ui->actionChangeTheme->menu(); + + if (themeMenu) + { + themeMenu->clear(); + } + else + { + themeMenu = new QMenu(this); + } + + ui->actionChangeTheme->setMenu(themeMenu); +} + void MainWindow::repopulateAccountsMenu() { accountMenu->clear(); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index f9d1f1c7..47e221b7 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -170,6 +170,8 @@ private slots: void updateToolsMenu(); + void updateThemeMenu(); + void instanceActivated(QModelIndex); void instanceChanged(const QModelIndex ¤t, const QModelIndex &previous); From 5be947291285fc8ed10852b54a647ecd338d645d Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 14 Nov 2022 21:20:38 +0100 Subject: [PATCH 103/277] fix: fix potentially uninitialized variable Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/modplatform/ModPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 234f9f36..677bc4d6 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -262,7 +262,7 @@ void ModPage::openUrl(const QUrl& url) const QString address = url.host() + url.path(); QRegularExpressionMatch match; - const char* page; + QString page; match = modrinth.match(address); if (match.hasMatch()) @@ -276,7 +276,7 @@ void ModPage::openUrl(const QUrl& url) page = "curseforge"; } - if (match.hasMatch()) { + if (!page.isNull()) { const QString slug = match.captured(1); // ensure the user isn't opening the same mod From cca052ccc9d644c84384ceefe97bbce45381a9e2 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:46:41 +0200 Subject: [PATCH 104/277] Restore QRC headers Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/resources/OSX/OSX.qrc | 3 ++- launcher/resources/iOS/iOS.qrc | 3 ++- launcher/resources/pe_blue/pe_blue.qrc | 3 ++- launcher/resources/pe_colored/pe_colored.qrc | 3 ++- launcher/resources/pe_dark/pe_dark.qrc | 3 ++- launcher/resources/pe_light/pe_light.qrc | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/launcher/resources/OSX/OSX.qrc b/launcher/resources/OSX/OSX.qrc index 3f50d6cf..9d4511d1 100644 --- a/launcher/resources/OSX/OSX.qrc +++ b/launcher/resources/OSX/OSX.qrc @@ -1,4 +1,5 @@ - + + index.theme scalable/about.svg diff --git a/launcher/resources/iOS/iOS.qrc b/launcher/resources/iOS/iOS.qrc index d7044fbb..0b79efb2 100644 --- a/launcher/resources/iOS/iOS.qrc +++ b/launcher/resources/iOS/iOS.qrc @@ -1,4 +1,5 @@ - + + index.theme scalable/about.svg diff --git a/launcher/resources/pe_blue/pe_blue.qrc b/launcher/resources/pe_blue/pe_blue.qrc index dc40103a..639675f0 100644 --- a/launcher/resources/pe_blue/pe_blue.qrc +++ b/launcher/resources/pe_blue/pe_blue.qrc @@ -1,4 +1,5 @@ - + + index.theme scalable/about.svg diff --git a/launcher/resources/pe_colored/pe_colored.qrc b/launcher/resources/pe_colored/pe_colored.qrc index bd1af6ff..fac58da8 100644 --- a/launcher/resources/pe_colored/pe_colored.qrc +++ b/launcher/resources/pe_colored/pe_colored.qrc @@ -1,4 +1,5 @@ - + + index.theme scalable/about.svg diff --git a/launcher/resources/pe_dark/pe_dark.qrc b/launcher/resources/pe_dark/pe_dark.qrc index 05ef7e93..c0c6ee6c 100644 --- a/launcher/resources/pe_dark/pe_dark.qrc +++ b/launcher/resources/pe_dark/pe_dark.qrc @@ -1,4 +1,5 @@ - + + index.theme scalable/about.svg diff --git a/launcher/resources/pe_light/pe_light.qrc b/launcher/resources/pe_light/pe_light.qrc index 6acca230..bd6a2496 100644 --- a/launcher/resources/pe_light/pe_light.qrc +++ b/launcher/resources/pe_light/pe_light.qrc @@ -1,4 +1,5 @@ - + + index.theme scalable/about.svg From 0e916244f03cd8bd296f6cc4fe2be34a745ecab4 Mon Sep 17 00:00:00 2001 From: leo78913 Date: Mon, 14 Nov 2022 19:59:26 -0300 Subject: [PATCH 105/277] feat: Add themes and their logic. Signed-off-by: leo78913 --- launcher/ui/MainWindow.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 8d5e0c83..cbdb2b8f 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -106,6 +106,7 @@ #include "ui/dialogs/UpdateDialog.h" #include "ui/dialogs/EditAccountDialog.h" #include "ui/dialogs/ExportInstanceDialog.h" +#include "ui/themes/ITheme.h" #include "UpdateController.h" #include "KonamiCode.h" @@ -1313,6 +1314,25 @@ void MainWindow::updateThemeMenu() themeMenu = new QMenu(this); } + auto themes = APPLICATION->getValidApplicationThemes(); + + QActionGroup* ThemesGroup = new QActionGroup( this ); + + for (int i = 0; i < themes.size(); i++) + { + + auto *theme = themes[i]; + QAction * themeAction = themeMenu->addAction(theme->name()); + + themeAction->setCheckable(true); + themeAction->setActionGroup(ThemesGroup); + + connect(themeAction, &QAction::triggered, [theme]() { + APPLICATION->setApplicationTheme(theme->name().toLower(),false); + + }); + } + ui->actionChangeTheme->setMenu(themeMenu); } From ef53455b6610844a633624fa172b7bf80310651c Mon Sep 17 00:00:00 2001 From: Redson Date: Tue, 15 Nov 2022 05:19:46 -0300 Subject: [PATCH 106/277] fix: Build on QT6 Signed-off-by: Redson --- launcher/ui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index cbdb2b8f..a00fb402 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -49,7 +49,7 @@ #include #include - +#include #include #include #include From aa3ea79f94887aedc91c52860d5a7b6557900a6f Mon Sep 17 00:00:00 2001 From: Redson Date: Tue, 15 Nov 2022 05:38:31 -0300 Subject: [PATCH 107/277] fix: Check the current theme box on startup. Signed-off-by: Redson --- launcher/ui/MainWindow.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index a00fb402..f8a37596 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1305,12 +1305,9 @@ void MainWindow::updateThemeMenu() { QMenu *themeMenu = ui->actionChangeTheme->menu(); - if (themeMenu) - { + if (themeMenu) { themeMenu->clear(); - } - else - { + } else { themeMenu = new QMenu(this); } @@ -1320,11 +1317,13 @@ void MainWindow::updateThemeMenu() for (int i = 0; i < themes.size(); i++) { - auto *theme = themes[i]; QAction * themeAction = themeMenu->addAction(theme->name()); themeAction->setCheckable(true); + if (APPLICATION->settings()->get("ApplicationTheme").toString() == theme->id()) { + themeAction->setChecked(true); + } themeAction->setActionGroup(ThemesGroup); connect(themeAction, &QAction::triggered, [theme]() { From a12f892841399163fbc12e72c61c7ed6fae882cf Mon Sep 17 00:00:00 2001 From: Redson Date: Tue, 15 Nov 2022 05:46:24 -0300 Subject: [PATCH 108/277] fix: Check the box when the theme is changed via settings Signed-off-by: Redson --- launcher/ui/MainWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index f8a37596..e2ab3b46 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1965,6 +1965,7 @@ void MainWindow::globalSettingsClosed() proxymodel->sort(0); updateMainToolBar(); updateToolsMenu(); + updateThemeMenu(); updateStatusCenter(); // This needs to be done to prevent UI elements disappearing in the event the config is changed // but Prism Launcher exits abnormally, causing the window state to never be saved: From 6fe626ab9ad9f76b793f09ceb5e8e8cced3f15ba Mon Sep 17 00:00:00 2001 From: Redson Date: Tue, 15 Nov 2022 06:09:13 -0300 Subject: [PATCH 109/277] feat: Make the changes persistent. Signed-off-by: Redson --- launcher/ui/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index e2ab3b46..f62d9760 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1327,8 +1327,8 @@ void MainWindow::updateThemeMenu() themeAction->setActionGroup(ThemesGroup); connect(themeAction, &QAction::triggered, [theme]() { - APPLICATION->setApplicationTheme(theme->name().toLower(),false); - + APPLICATION->setApplicationTheme(theme->id(),false); + APPLICATION->settings()->set("ApplicationTheme", theme->id()); }); } From 7439fd6bcba789488c8b67128a7e5d65054a46a0 Mon Sep 17 00:00:00 2001 From: leo78913 Date: Tue, 15 Nov 2022 10:56:41 -0300 Subject: [PATCH 110/277] fix: Fix some stuff in the menu bar - Remove duplicated launch actions - Enable the add instance action icon in menus - Remove ugly separator at the end of the help menu if the updater is disabled - Add icons to the help and news menu items Signed-off-by: leo78913 --- launcher/ui/MainWindow.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 85b00b67..6e3b95ff 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -290,7 +290,6 @@ public: actionAddInstance = TranslatedAction(MainWindow); actionAddInstance->setObjectName(QStringLiteral("actionAddInstance")); actionAddInstance->setIcon(APPLICATION->getThemedIcon("new")); - actionAddInstance->setIconVisibleInMenu(false); actionAddInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Add Instanc&e...")); actionAddInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Add a new instance.")); actionAddInstance->setShortcut(QKeySequence::New); @@ -509,8 +508,6 @@ public: fileMenu->setSeparatorsCollapsible(false); fileMenu->addAction(actionAddInstance); fileMenu->addAction(actionLaunchInstance); - fileMenu->addAction(actionLaunchInstanceOffline); - fileMenu->addAction(actionLaunchInstanceDemo); fileMenu->addAction(actionKillInstance); fileMenu->addAction(actionCloseWindow); fileMenu->addSeparator(); @@ -555,10 +552,11 @@ public: helpMenu->addAction(actionDISCORD); if (!BuildConfig.SUBREDDIT_URL.isEmpty()) helpMenu->addAction(actionREDDIT); - helpMenu->addSeparator(); if(BuildConfig.UPDATER_ENABLED) + { + helpMenu->addSeparator(); helpMenu->addAction(actionCheckUpdate); - + } MainWindow->setMenuBar(menuBar); } @@ -576,6 +574,7 @@ public: actionOpenWiki->setObjectName(QStringLiteral("actionOpenWiki")); actionOpenWiki.setTextId(QT_TRANSLATE_NOOP("MainWindow", "%1 &Help")); actionOpenWiki.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the %1 wiki")); + actionOpenWiki->setIcon(APPLICATION->getThemedIcon("help")); connect(actionOpenWiki, &QAction::triggered, MainWindow, &MainWindow::on_actionOpenWiki_triggered); all_actions.append(&actionOpenWiki); @@ -583,6 +582,7 @@ public: actionNewsMenuBar->setObjectName(QStringLiteral("actionNewsMenuBar")); actionNewsMenuBar.setTextId(QT_TRANSLATE_NOOP("MainWindow", "%1 &News")); actionNewsMenuBar.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the %1 wiki")); + actionNewsMenuBar->setIcon(APPLICATION->getThemedIcon("news")); connect(actionNewsMenuBar, &QAction::triggered, MainWindow, &MainWindow::on_actionMoreNews_triggered); all_actions.append(&actionNewsMenuBar); } From 7705f290ca69c0478f698ddf06a5f1edb731e8fc Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 15 Nov 2022 12:07:11 -0300 Subject: [PATCH 111/277] fix: don't retry to load RP images that can't fit in the cache Avoids an infinite loop when the image is too big. Signed-off-by: flow --- launcher/minecraft/mod/ResourcePack.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index 3fc10a2f..b97bedf8 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -47,6 +47,12 @@ void ResourcePack::setImage(QImage new_image) m_pack_image_cache_key.key = QPixmapCache::insert(QPixmap::fromImage(new_image)); m_pack_image_cache_key.was_ever_used = true; + + // This can happen if the pixmap is too big to fit in the cache :c + if (!m_pack_image_cache_key.key.isValid()) { + qWarning() << "Could not insert a image cache entry! Ignoring it."; + m_pack_image_cache_key.was_ever_used = false; + } } QPixmap ResourcePack::image(QSize size) From 464b9ebc95ca0743dd13337a9db7159aab76292a Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Mon, 14 Nov 2022 17:29:41 +0100 Subject: [PATCH 112/277] feat: add flatpak builds to the ci for testing Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 23 +++++- flatpak/org.prismlauncher.PrismLauncher.yml | 83 +++++++++++++++++++++ flatpak/prime-run | 4 + flatpak/prismlauncher | 11 +++ 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 flatpak/org.prismlauncher.PrismLauncher.yml create mode 100644 flatpak/prime-run create mode 100644 flatpak/prismlauncher diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61fd9b4c..df8aae39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -492,16 +492,35 @@ jobs: submodules: 'true' - name: Set short version shell: bash + if: inputs.build_type == 'Debug' run: | ver_short=`git rev-parse --short HEAD` echo "VERSION=$ver_short" >> $GITHUB_ENV - name: Package Snap (Linux) id: snapcraft - if: runner.os == 'Linux' && inputs.build_type == 'Debug' + if: inputs.build_type == 'Debug' uses: snapcore/action-build@v1 - name: Upload Snap (Linux) - if: runner.os == 'Linux' && inputs.build_type == 'Debug' + if: inputs.build_type == 'Debug' uses: actions/upload-artifact@v3 with: name: prismlauncher_${{ env.VERSION }}_amd64.snap path: ${{ steps.snapcraft.outputs.snap }} + + flatpak: + runs-on: ubuntu-latest + container: + image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08 + options: --privileged + steps: + - name: Checkout + uses: actions/checkout@v3 + if: inputs.build_type == 'Debug' + with: + submodules: 'true' + - name: Build Flatpak (Linux) + if: inputs.build_type == 'Debug' + uses: flatpak/flatpak-github-actions/flatpak-builder@v4 + with: + bundle: "Prism Launcher.flatpak" + manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml new file mode 100644 index 00000000..fca306d7 --- /dev/null +++ b/flatpak/org.prismlauncher.PrismLauncher.yml @@ -0,0 +1,83 @@ +id: org.prismlauncher.PrismLauncher +runtime: org.kde.Platform +runtime-version: "5.15-22.08" +sdk: org.kde.Sdk +sdk-extensions: + - org.freedesktop.Sdk.Extension.openjdk17 + - org.freedesktop.Sdk.Extension.openjdk8 +add-extensions: + com.valvesoftware.Steam.Utility.gamescope: + version: stable + add-ld-path: lib + no-autodownload: true + autodelete: false + directory: utils/gamescope + +command: prismlauncher +finish-args: + - --share=ipc + - --socket=x11 + - --socket=wayland + - --device=all + - --share=network + - --socket=pulseaudio + # for Discord RPC mods + - --filesystem=xdg-run/app/com.discordapp.Discord:create + # Mod drag&drop + - --filesystem=xdg-download:ro + +modules: + - name: prismlauncher + buildsystem: cmake-ninja + config-opts: + - -DLauncher_BUILD_PLATFORM=flatpak + - -DCMAKE_BUILD_TYPE=Debug + build-options: + env: + JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17 + JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac + sources: + - type: dir + path: ../ + - name: openjdk + buildsystem: simple + build-commands: + - mkdir -p /app/jdk/ + - /usr/lib/sdk/openjdk17/install.sh + - mv /app/jre /app/jdk/17 + - /usr/lib/sdk/openjdk8/install.sh + - mv /app/jre /app/jdk/8 + cleanup: [/jre] + - name: xrandr + buildsystem: autotools + sources: + - type: archive + url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz + sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762 + cleanup: [/share/man, /bin/xkeystone] + - name: gamemode + buildsystem: meson + config-opts: + - -Dwith-sd-bus-provider=no-daemon + - -Dwith-examples=false + post-install: + # gamemoderun is installed for users who want to use wrapper commands + # post-install is running inside the build dir, we need it from the source though + - install -Dm755 ../data/gamemoderun -t /app/bin + sources: + - type: git + url: https://github.com/FeralInteractive/gamemode + tag: "1.7" + commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9 + - name: enhance + buildsystem: simple + build-commands: + - mkdir -p /app/utils/gamescope + - install -Dm755 prime-run /app/bin/prime-run + - mv /app/bin/prismlauncher /app/bin/prismrun + - install -Dm755 prismlauncher /app/bin/prismlauncher + sources: + - type: file + path: ../flatpak/prime-run + - type: file + path: ../flatpak/prismlauncher diff --git a/flatpak/prime-run b/flatpak/prime-run new file mode 100644 index 00000000..946c28dd --- /dev/null +++ b/flatpak/prime-run @@ -0,0 +1,4 @@ +#!/bin/sh + +export __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia +exec "$@" diff --git a/flatpak/prismlauncher b/flatpak/prismlauncher new file mode 100644 index 00000000..bb876711 --- /dev/null +++ b/flatpak/prismlauncher @@ -0,0 +1,11 @@ +#!/bin/bash + +# discord RPC +for i in {0..9}; do + test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i"; +done + +export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin" +export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/" + +exec /app/bin/prismrun "$@" From ee096edfff11ba6773793b9f0bf7aaa8bd273110 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Tue, 15 Nov 2022 20:58:05 +0100 Subject: [PATCH 113/277] chore: bump tomlplusplus Should fix issues with builds on aarch64 Signed-off-by: Sefa Eyeoglu --- libraries/tomlplusplus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/tomlplusplus b/libraries/tomlplusplus index cc741c9f..0a90913a 160000 --- a/libraries/tomlplusplus +++ b/libraries/tomlplusplus @@ -1 +1 @@ -Subproject commit cc741c9f5f2a62856a2a2e9e275f61eb0591c09c +Subproject commit 0a90913abf9390b9e08ab6d3b40ac11634553f38 From c1763cc4b0871230a86e58aad07aacaf7b19182c Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Tue, 15 Nov 2022 21:23:20 +0100 Subject: [PATCH 114/277] fix: disable FLOAT16 in toml++ Signed-off-by: Sefa Eyeoglu --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0db05f98..8fc0d326 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00") +# Fix aarch64 build for toml++ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0") + # set CXXFLAGS for build targets set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}") From c4c1e75de8825a4af403046536a7b2acd72a56c3 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 15 Nov 2022 18:13:15 -0300 Subject: [PATCH 115/277] fix: use QPixmapCache only from the main thread It's a required condition. Signed-off-by: flow --- launcher/Application.cpp | 6 ++ launcher/CMakeLists.txt | 2 + launcher/MTPixmapCache.h | 95 +++++++++++++++++++++++++ launcher/minecraft/mod/ResourcePack.cpp | 8 ++- 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 launcher/MTPixmapCache.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 45cd9422..c3d624ad 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -90,6 +90,7 @@ #include #include "InstanceList.h" +#include "MTPixmapCache.h" #include #include "icons/IconList.h" @@ -134,6 +135,8 @@ static const QLatin1String liveCheckFile("live.check"); +PixmapCache* PixmapCache::s_instance = nullptr; + namespace { void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { @@ -693,6 +696,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); } + + PixmapCache::setInstance(new PixmapCache(this)); + qDebug() << "<> Settings loaded."; } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 8db93429..0605be23 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -89,6 +89,8 @@ set(CORE_SOURCES # Time MMCTime.h MMCTime.cpp + + MTPixmapCache.h ) set(PATHMATCHER_SOURCES diff --git a/launcher/MTPixmapCache.h b/launcher/MTPixmapCache.h new file mode 100644 index 00000000..57847a0e --- /dev/null +++ b/launcher/MTPixmapCache.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include + +#define GET_TYPE() \ + Qt::ConnectionType type; \ + if (QThread::currentThread() != QCoreApplication::instance()->thread()) \ + type = Qt::BlockingQueuedConnection; \ + else \ + type = Qt::DirectConnection; + +#define DEFINE_FUNC_NO_PARAM(NAME, RET_TYPE) \ + static RET_TYPE NAME() \ + { \ + RET_TYPE ret; \ + GET_TYPE() \ + QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret)); \ + return ret; \ + } +#define DEFINE_FUNC_ONE_PARAM(NAME, RET_TYPE, PARAM_1_TYPE) \ + static RET_TYPE NAME(PARAM_1_TYPE p1) \ + { \ + RET_TYPE ret; \ + GET_TYPE() \ + QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1)); \ + return ret; \ + } +#define DEFINE_FUNC_TWO_PARAM(NAME, RET_TYPE, PARAM_1_TYPE, PARAM_2_TYPE) \ + static RET_TYPE NAME(PARAM_1_TYPE p1, PARAM_2_TYPE p2) \ + { \ + RET_TYPE ret; \ + GET_TYPE() \ + QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1), \ + Q_ARG(PARAM_2_TYPE, p2)); \ + return ret; \ + } + +/** A wrapper around QPixmapCache with thread affinity with the main thread. + */ +class PixmapCache final : public QObject { + Q_OBJECT + + public: + PixmapCache(QObject* parent) : QObject(parent) {} + ~PixmapCache() override = default; + + static PixmapCache& instance() { return *s_instance; } + static void setInstance(PixmapCache* i) { s_instance = i; } + + public: + DEFINE_FUNC_NO_PARAM(cacheLimit, int) + DEFINE_FUNC_NO_PARAM(clear, bool) + DEFINE_FUNC_TWO_PARAM(find, bool, const QString&, QPixmap*) + DEFINE_FUNC_TWO_PARAM(find, bool, const QPixmapCache::Key&, QPixmap*) + DEFINE_FUNC_TWO_PARAM(insert, bool, const QString&, const QPixmap&) + DEFINE_FUNC_ONE_PARAM(insert, QPixmapCache::Key, const QPixmap&) + DEFINE_FUNC_ONE_PARAM(remove, bool, const QString&) + DEFINE_FUNC_ONE_PARAM(remove, bool, const QPixmapCache::Key&) + DEFINE_FUNC_TWO_PARAM(replace, bool, const QPixmapCache::Key&, const QPixmap&) + DEFINE_FUNC_ONE_PARAM(setCacheLimit, bool, int) + + // NOTE: Every function returns something non-void to simplify the macros. + private slots: + int _cacheLimit() { return QPixmapCache::cacheLimit(); } + bool _clear() + { + QPixmapCache::clear(); + return true; + } + bool _find(const QString& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); } + bool _find(const QPixmapCache::Key& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); } + bool _insert(const QString& key, const QPixmap& pixmap) { return QPixmapCache::insert(key, pixmap); } + QPixmapCache::Key _insert(const QPixmap& pixmap) { return QPixmapCache::insert(pixmap); } + bool _remove(const QString& key) + { + QPixmapCache::remove(key); + return true; + } + bool _remove(const QPixmapCache::Key& key) + { + QPixmapCache::remove(key); + return true; + } + bool _replace(const QPixmapCache::Key& key, const QPixmap& pixmap) { return QPixmapCache::replace(key, pixmap); } + bool _setCacheLimit(int n) + { + QPixmapCache::setCacheLimit(n); + return true; + } + + private: + static PixmapCache* s_instance; +}; diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index b97bedf8..242fd9fe 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -1,9 +1,11 @@ #include "ResourcePack.h" +#include #include #include #include +#include "MTPixmapCache.h" #include "Version.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" @@ -43,9 +45,9 @@ void ResourcePack::setImage(QImage new_image) Q_ASSERT(!new_image.isNull()); if (m_pack_image_cache_key.key.isValid()) - QPixmapCache::remove(m_pack_image_cache_key.key); + PixmapCache::instance().remove(m_pack_image_cache_key.key); - m_pack_image_cache_key.key = QPixmapCache::insert(QPixmap::fromImage(new_image)); + m_pack_image_cache_key.key = PixmapCache::instance().insert(QPixmap::fromImage(new_image)); m_pack_image_cache_key.was_ever_used = true; // This can happen if the pixmap is too big to fit in the cache :c @@ -58,7 +60,7 @@ void ResourcePack::setImage(QImage new_image) QPixmap ResourcePack::image(QSize size) { QPixmap cached_image; - if (QPixmapCache::find(m_pack_image_cache_key.key, &cached_image)) { + if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) { if (size.isNull()) return cached_image; return cached_image.scaled(size); From 9f30c6d94b3c3fd5dbc5f998c9e9c8083b4d57df Mon Sep 17 00:00:00 2001 From: Fayne Aldan Date: Tue, 15 Nov 2022 18:33:51 -0700 Subject: [PATCH 116/277] Add commit hash to `--version` Signed-off-by: Fayne Aldan --- launcher/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 45cd9422..883f8968 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -226,7 +226,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); setApplicationName(BuildConfig.LAUNCHER_NAME); setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString())); - setApplicationVersion(BuildConfig.printableVersionString()); + setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME); startTime = QDateTime::currentDateTime(); From 88feebe499ece4eb065493b84aab4dd5c9b0c32a Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 16 Nov 2022 09:25:11 +0100 Subject: [PATCH 117/277] chore: use qt 6.4.1 on Windows-MSVC Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61fd9b4c..965d25f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.4.0' + qt_version: '6.4.1' qt_modules: 'qt5compat qtimageformats' qt_tools: '' From 37a117d2effc21b23b86ada80951bfd23671a64b Mon Sep 17 00:00:00 2001 From: RedsonBr140 Date: Wed, 16 Nov 2022 12:24:28 -0300 Subject: [PATCH 118/277] chore: Var definition inside the for loop Co-authored-by: flow Signed-off-by: RedsonBr140 --- launcher/ui/MainWindow.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index f62d9760..1f8444af 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1315,9 +1315,7 @@ void MainWindow::updateThemeMenu() QActionGroup* ThemesGroup = new QActionGroup( this ); - for (int i = 0; i < themes.size(); i++) - { - auto *theme = themes[i]; + for (auto* theme : themes) { QAction * themeAction = themeMenu->addAction(theme->name()); themeAction->setCheckable(true); From 8aae652be1ee876996bc833fcd74e1de9b1c61dc Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 16 Nov 2022 18:10:54 +0100 Subject: [PATCH 119/277] fix: remove unnecessary condition Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/OneSixVersionFormat.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index cec4a55b..c70e5ec5 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -225,11 +225,10 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc { QJsonObject agentObj = requireObject(agentVal); auto lib = libraryFromJson(*out, agentObj, filename); + QString arg = ""; - if (agentObj.contains("argument")) - { - readString(agentObj, "argument", arg); - } + readString(agentObj, "argument", arg); + AgentPtr agent(new Agent(lib, arg)); out->agents.append(agent); } From 40970a1a87a48f434dae8583f2c71942931c3fe1 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 16 Nov 2022 18:11:17 +0100 Subject: [PATCH 120/277] fix: serialize metadata extensions Prism has introduced a few extensions to the meta component version format, which were not exported as JSON in the launcher. This caused the `Customize` button on the version page to not write these new properties to the custom component file. Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/MojangVersionFormat.cpp | 9 +++++++++ launcher/minecraft/OneSixVersionFormat.cpp | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/launcher/minecraft/MojangVersionFormat.cpp b/launcher/minecraft/MojangVersionFormat.cpp index 9bbb4ada..9b442d63 100644 --- a/launcher/minecraft/MojangVersionFormat.cpp +++ b/launcher/minecraft/MojangVersionFormat.cpp @@ -306,6 +306,15 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj } out.insert("downloads", downloadsOut); } + if(in->compatibleJavaMajors.size()) + { + QJsonArray compatibleJavaMajorsOut; + for(auto compatibleJavaMajor : in->compatibleJavaMajors) + { + compatibleJavaMajorsOut.append(compatibleJavaMajor); + } + out.insert("compatibleJavaMajors", compatibleJavaMajorsOut); + } } QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch) diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index c70e5ec5..ac29791b 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -331,6 +331,20 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch writeString(root, "appletClass", patch->appletClass); writeStringList(root, "+tweakers", patch->addTweakers); writeStringList(root, "+traits", patch->traits.values()); + writeStringList(root, "+jvmArgs", patch->addnJvmArguments); + if (!patch->agents.isEmpty()) + { + QJsonArray array; + for (auto value: patch->agents) + { + QJsonObject agentOut = OneSixVersionFormat::libraryToJson(value->library().get()); + if (!value->argument().isEmpty()) + agentOut.insert("argument", value->argument()); + + array.append(agentOut); + } + root.insert("+agents", array); + } if (!patch->libraries.isEmpty()) { QJsonArray array; From ce958f4ffd8ee73ea14a8748dd63a79623358ee9 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 16 Nov 2022 23:20:31 +0100 Subject: [PATCH 121/277] refactor: use isEmpty instead of size for lists Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/MojangVersionFormat.cpp | 10 +++++----- launcher/minecraft/OneSixVersionFormat.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/launcher/minecraft/MojangVersionFormat.cpp b/launcher/minecraft/MojangVersionFormat.cpp index 9b442d63..623dcdfa 100644 --- a/launcher/minecraft/MojangVersionFormat.cpp +++ b/launcher/minecraft/MojangVersionFormat.cpp @@ -135,7 +135,7 @@ QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo) { out.insert("artifact", downloadInfoToJson(libinfo->artifact)); } - if(libinfo->classifiers.size()) + if(!libinfo->classifiers.isEmpty()) { QJsonObject classifiersOut; for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++) @@ -297,7 +297,7 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj { out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex)); } - if(in->mojangDownloads.size()) + if(!in->mojangDownloads.isEmpty()) { QJsonObject downloadsOut; for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++) @@ -306,7 +306,7 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj } out.insert("downloads", downloadsOut); } - if(in->compatibleJavaMajors.size()) + if(!in->compatibleJavaMajors.isEmpty()) { QJsonArray compatibleJavaMajorsOut; for(auto compatibleJavaMajor : in->compatibleJavaMajors) @@ -405,7 +405,7 @@ QJsonObject MojangVersionFormat::libraryToJson(Library *library) iter++; } libRoot.insert("natives", nativeList); - if (library->m_extractExcludes.size()) + if (!library->m_extractExcludes.isEmpty()) { QJsonArray excludes; QJsonObject extract; @@ -417,7 +417,7 @@ QJsonObject MojangVersionFormat::libraryToJson(Library *library) libRoot.insert("extract", extract); } } - if (library->m_rules.size()) + if (!library->m_rules.isEmpty()) { QJsonArray allRules; for (auto &rule : library->m_rules) diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index ac29791b..280f6b26 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -63,13 +63,13 @@ LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer & problems, con QJsonObject OneSixVersionFormat::libraryToJson(Library *library) { QJsonObject libRoot = MojangVersionFormat::libraryToJson(library); - if (library->m_absoluteURL.size()) + if (!library->m_absoluteURL.isEmpty()) libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL); - if (library->m_hint.size()) + if (!library->m_hint.isEmpty()) libRoot.insert("MMC-hint", library->m_hint); - if (library->m_filename.size()) + if (!library->m_filename.isEmpty()) libRoot.insert("MMC-filename", library->m_filename); - if (library->m_displayname.size()) + if (!library->m_displayname.isEmpty()) libRoot.insert("MMC-displayname", library->m_displayname); return libRoot; } From bd3a693e700bc0b1ea9bbc72631ccd13ba755277 Mon Sep 17 00:00:00 2001 From: RedsonBr140 Date: Thu, 17 Nov 2022 06:20:53 -0300 Subject: [PATCH 122/277] chore: Change var name Co-authored-by: Sefa Eyeoglu Signed-off-by: RedsonBr140 --- launcher/ui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 1f8444af..dcf1be99 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1313,7 +1313,7 @@ void MainWindow::updateThemeMenu() auto themes = APPLICATION->getValidApplicationThemes(); - QActionGroup* ThemesGroup = new QActionGroup( this ); + QActionGroup* themesGroup = new QActionGroup( this ); for (auto* theme : themes) { QAction * themeAction = themeMenu->addAction(theme->name()); From f72ac94c111a788d197e78fd4e0433513965807a Mon Sep 17 00:00:00 2001 From: Redson Date: Thu, 17 Nov 2022 06:39:05 -0300 Subject: [PATCH 123/277] fix: Fix usage below Signed-off-by: Redson --- launcher/ui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index dcf1be99..e1ea13d3 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1322,7 +1322,7 @@ void MainWindow::updateThemeMenu() if (APPLICATION->settings()->get("ApplicationTheme").toString() == theme->id()) { themeAction->setChecked(true); } - themeAction->setActionGroup(ThemesGroup); + themeAction->setActionGroup(themesGroup); connect(themeAction, &QAction::triggered, [theme]() { APPLICATION->setApplicationTheme(theme->id(),false); From 90376749f07017c85460d6fa31f81e45c6053840 Mon Sep 17 00:00:00 2001 From: Sophie Marie Date: Thu, 17 Nov 2022 12:42:34 -0600 Subject: [PATCH 124/277] Fix AUR qt5-git and git having the wrong AUR links Signed-off-by: Sophie Marie --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8ea2e84..cb99f70f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS* For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions: -[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git/) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git/) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?style=flat-square&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git) [![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?style=flat-square&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?style=flat-square&logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?style=flat-square&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher) +[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git/) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git/) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?style=flat-square&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git) [![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?style=flat-square&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?style=flat-square&logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?style=flat-square&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher) ## Community & Support From 52d43f843bceb861c92f8989a27194436924e087 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 17 Nov 2022 11:46:14 -0700 Subject: [PATCH 125/277] feat(BlockModsDialog): remove redundant Ui info Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/dialogs/BlockedModsDialog.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index a0dd1a10..edb4ff7d 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -38,12 +38,9 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons QString(tr("Your configured global mods folder and default downloads folder " "are automatically checked for the downloaded mods and they will be copied to the instance if found.
" "Optionally, you may drag and drop the downloaded mods onto this dialog or add a folder to watch " - "if you did not download the mods to a default location.

" - "Global Mods Folder: %1
" - "Default Downloads Folder: %2")) + "if you did not download the mods to a default location.")) .arg(APPLICATION->settings()->get("CentralModsDir").toString(), QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))); - ui->labelModsFound->setText(tr("Please download the missing mods.")); // force all URL handeling as external connect(ui->textBrowserWatched, &QTextBrowser::anchorClicked, this, [](const QUrl url) { QDesktopServices::openUrl(url); }); @@ -127,7 +124,7 @@ void BlockedModsDialog::update() ui->textBrowserWatched->setText(watching); if (allModsMatched()) { - ui->labelModsFound->setText(tr("All mods found ✔")); + ui->labelModsFound->setText("" + tr("All mods found")); } else { ui->labelModsFound->setText(tr("Please download the missing mods.")); } From c6d9edb78f3bb6772667d02c0713adcdcaf89e8b Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 18 Nov 2022 22:21:55 +0100 Subject: [PATCH 126/277] chore: improve display names of more languages Signed-off-by: Sefa Eyeoglu --- launcher/translations/TranslationsModel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 20aa6d04..84778d32 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -89,6 +89,12 @@ struct Language else if(key == "en@uwu") { result = u8"Cute Engwish"; } + else if(key == "tok") { + result = u8"toki pona"; + } + else if(key == "nan") { + result = u8"閩南語"; // Using traditional Chinese script. Not sure if we should use simplified instead? + } else { result = locale.nativeLanguageName(); } From 669eef92eb426ea500c3bdaf5ec5b07d98e7c637 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 19 Nov 2022 08:54:17 +0000 Subject: [PATCH 127/277] Make requested changes and utilise AssertionError Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 1 - .../launcher/net/minecraft/Launcher.java | 10 ++-- .../org/prismlauncher/EntryPoint.java | 5 +- .../launcher/impl/AbstractLauncher.java | 5 +- .../org/prismlauncher/utils/StringUtils.java | 49 ------------------- 5 files changed, 7 insertions(+), 63 deletions(-) delete mode 100644 libraries/launcher/org/prismlauncher/utils/StringUtils.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 1d22a93e..55ed5875 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -17,7 +17,6 @@ set(SRC org/prismlauncher/exception/ParseException.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java - org/prismlauncher/utils/StringUtils.java org/prismlauncher/utils/logging/Level.java org/prismlauncher/utils/logging/Log.java net/minecraft/Launcher.java diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index a721495a..646e2e3e 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -64,8 +64,6 @@ import java.net.URL; import java.util.HashMap; import java.util.Map; -import org.prismlauncher.utils.logging.Log; - /** * WARNING: This class is reflectively accessed by legacy Forge versions. *

@@ -97,14 +95,13 @@ public final class Launcher extends Applet implements AppletStub { if (documentBase == null) { if (applet.getClass().getPackage().getName().startsWith("com.mojang.")) { // Special case only for Classic versions - documentBase = new URL("http", "www.minecraft.net", 80, "/game/"); + documentBase = new URL("http://www.minecraft.net:80/game/"); } else { documentBase = new URL("http://www.minecraft.net/game/"); } } } catch (MalformedURLException e) { - // handle gracefully - it won't happen, but Java requires that it is caught - Log.error("Failed to parse document base URL", e); + throw new AssertionError(e); } this.documentBase = documentBase; @@ -143,8 +140,7 @@ public final class Launcher extends Applet implements AppletStub { try { return new URL("http://www.minecraft.net/game/"); } catch (MalformedURLException e) { - Log.error("Failed to parse codebase URL", e); - return null; + throw new AssertionError(e); } } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index f6567468..78804b3e 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -63,7 +63,6 @@ import org.prismlauncher.launcher.Launcher; import org.prismlauncher.launcher.impl.StandardLauncher; import org.prismlauncher.launcher.impl.legacy.LegacyLauncher; import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.StringUtils; import org.prismlauncher.utils.logging.Log; public final class EntryPoint { @@ -150,9 +149,9 @@ public final class EntryPoint { return PreLaunchAction.ABORT; default: - String[] pair = StringUtils.splitStringPair(' ', input); + String[] pair = input.split(" ", 2); - if (pair == null) + if (pair.length != 2) throw new ParseException(input, "[key] [value]"); params.add(pair[0], pair[1]); diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index 585d55f1..0c2153a9 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -60,7 +60,6 @@ import java.util.List; import org.prismlauncher.exception.ParseException; import org.prismlauncher.launcher.Launcher; import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.StringUtils; public abstract class AbstractLauncher implements Launcher { @@ -93,9 +92,9 @@ public abstract class AbstractLauncher implements Launcher { } else { maximize = false; - String[] sizePair = StringUtils.splitStringPair('x', windowParams); + String[] sizePair = windowParams.split("x", 2); - if (sizePair != null) { + if (sizePair.length == 2) { try { width = Integer.parseInt(sizePair[0]); height = Integer.parseInt(sizePair[1]); diff --git a/libraries/launcher/org/prismlauncher/utils/StringUtils.java b/libraries/launcher/org/prismlauncher/utils/StringUtils.java deleted file mode 100644 index dfd1634b..00000000 --- a/libraries/launcher/org/prismlauncher/utils/StringUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 solonovamax - * - * 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. - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole - * combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish to do - * so, delete this exception statement from your version. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.prismlauncher.utils; - -public final class StringUtils { - - public static String[] splitStringPair(char splitChar, String str) { - int splitPoint = str.indexOf(splitChar); - - if (splitPoint == -1) - return null; - - return new String[] { str.substring(0, splitPoint), str.substring(splitPoint + 1) }; - } - -} From f7239f7f8a30de55a270a9800ad1eaeba4c17586 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 19 Nov 2022 13:34:01 +0100 Subject: [PATCH 128/277] chore: update community links Signed-off-by: Sefa Eyeoglu --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb99f70f..f02b5695 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,15 @@ For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, ## Community & Support -Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple communities that can also help you. +Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple community spaces where other community members can help you. #### Join our Discord server: [![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner2)](https://discord.gg/prismlauncher) -#### Join our Matrix space (Will be opened at a later date): +#### Join our Matrix space: [![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&logo=matrix)](https://matrix.to/#/#prismlauncher:matrix.org) -#### Join our SubReddit: +#### Join our Subreddit: [![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://www.reddit.com/r/PrismLauncher/) ## Building From 70fa92f22ca8d013e7e101645d4ef60a21a52223 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 19 Nov 2022 13:58:35 +0000 Subject: [PATCH 129/277] Remove dirty printStackTrace hacks Signed-off-by: TheKodeToad --- libraries/launcher/.gitignore | 1 + .../org/prismlauncher/utils/logging/Log.java | 10 +- .../utils/logging/LogPrintStream.java | 99 ------------------- 3 files changed, 5 insertions(+), 105 deletions(-) delete mode 100644 libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java diff --git a/libraries/launcher/.gitignore b/libraries/launcher/.gitignore index cc1c52bf..dda456e3 100644 --- a/libraries/launcher/.gitignore +++ b/libraries/launcher/.gitignore @@ -4,3 +4,4 @@ out .classpath .idea .project +bin/ diff --git a/libraries/launcher/org/prismlauncher/utils/logging/Log.java b/libraries/launcher/org/prismlauncher/utils/logging/Log.java index 374a8107..e3aa538b 100644 --- a/libraries/launcher/org/prismlauncher/utils/logging/Log.java +++ b/libraries/launcher/org/prismlauncher/utils/logging/Log.java @@ -44,10 +44,8 @@ import java.io.PrintStream; */ public final class Log { - // original before overridden - private static final PrintStream OUT = new PrintStream(System.out), ERR = new PrintStream(System.err); - private static final PrintStream ERROR_PREFIX = new LogPrintStream(System.err, Level.ERROR), - FATAL_PREFIX = new LogPrintStream(System.err, Level.FATAL); + // original before possibly overridden by MC + private static final PrintStream OUT = new PrintStream(System.out), ERR = new PrintStream(System.err); private static final boolean DEBUG = Boolean.getBoolean("org.prismlauncher.debug"); public static void launcher(String message) { @@ -68,7 +66,7 @@ public final class Log { public static void error(String message, Throwable e) { error(message); - e.printStackTrace(ERROR_PREFIX); + e.printStackTrace(ERR); } public static void fatal(String message) { @@ -77,7 +75,7 @@ public final class Log { public static void fatal(String message, Throwable e) { fatal(message); - e.printStackTrace(FATAL_PREFIX); + e.printStackTrace(ERR); } /** diff --git a/libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java b/libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java deleted file mode 100644 index 8a182817..00000000 --- a/libraries/launcher/org/prismlauncher/utils/logging/LogPrintStream.java +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 TheKodeToad - * - * 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. - * - * Linking this library statically or dynamically with other modules is - * making a combined work based on this library. Thus, the terms and - * conditions of the GNU General Public License cover the whole - * combination. - * - * As a special exception, the copyright holders of this library give - * you permission to link this library with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also meet, - * for each linked independent module, the terms and conditions of the - * license of that module. An independent module is a module which is - * not derived from or based on this library. If you modify this - * library, you may extend this exception to your version of the - * library, but you are not obliged to do so. If you do not wish to do - * so, delete this exception statement from your version. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.prismlauncher.utils.logging; - -import java.io.OutputStream; -import java.io.PrintStream; - -/** - * Used to create a print stream that redirects to Log. - */ -final class LogPrintStream extends PrintStream { - - private final Level level; - - public LogPrintStream(OutputStream out, Level level) { - super(out); - - this.level = level; - } - - @Override - public void println(String x) { - Log.log(x, level); - } - - @Override - public void println(Object x) { - println(String.valueOf(x)); - } - - @Override - public void println(boolean x) { - println(String.valueOf(x)); - } - - @Override - public void println(char x) { - println(String.valueOf(x)); - } - - @Override - public void println(int x) { - println(String.valueOf(x)); - } - - @Override - public void println(long x) { - println(String.valueOf(x)); - } - - @Override - public void println(float x) { - println(String.valueOf(x)); - } - - @Override - public void println(double x) { - println(String.valueOf(x)); - } - - @Override - public void println(char[] x) { - println(String.valueOf(x)); - } - -} \ No newline at end of file From 8dacbafc8ba45ae6c2b770da77cc0d3d632849ba Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 19 Nov 2022 23:04:26 +0800 Subject: [PATCH 130/277] feat: initial support for smart resource pack parsing on file handler Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/Application.cpp | 23 ++++++- launcher/CMakeLists.txt | 3 + .../ui/dialogs/ImportResourcePackDialog.cpp | 61 +++++++++++++++++ .../ui/dialogs/ImportResourcePackDialog.h | 24 +++++++ .../ui/dialogs/ImportResourcePackDialog.ui | 67 +++++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 launcher/ui/dialogs/ImportResourcePackDialog.cpp create mode 100644 launcher/ui/dialogs/ImportResourcePackDialog.h create mode 100644 launcher/ui/dialogs/ImportResourcePackDialog.ui diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 883f8968..71cd009a 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -66,6 +66,7 @@ #include "ui/setupwizard/PasteWizardPage.h" #include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ImportResourcePackDialog.h" #include "ui/pagedialog/PageDialog.h" @@ -96,6 +97,10 @@ #include "net/HttpMetaCache.h" #include "java/JavaUtils.h" +#include +#include +#include +#include #include "updater/UpdateChecker.h" @@ -930,7 +935,23 @@ bool Application::event(QEvent* event) { if (event->type() == QEvent::FileOpen) { auto ev = static_cast(event); - m_mainWindow->droppedURLs({ ev->url() }); + + ResourcePack pack{ QFileInfo(ev->file()) }; + + ResourcePackUtils::process(pack); + // + + if (pack.valid()) { + ImportResourcePackDialog dlg(APPLICATION->m_mainWindow); + dlg.exec(); + if (dlg.result() == QDialog::Accepted) { + auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); + auto instanceButBuffed = std::dynamic_pointer_cast(instance); + instanceButBuffed->resourcePackList()->installResource(ev->file()); + } + } else { + m_mainWindow->droppedURLs({ ev->url() }); + } } return QApplication::event(event); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 8db93429..58d5d964 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -791,6 +791,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/ExportInstanceDialog.h ui/dialogs/IconPickerDialog.cpp ui/dialogs/IconPickerDialog.h + ui/dialogs/ImportResourcePackDialog.cpp + ui/dialogs/ImportResourcePackDialog.h ui/dialogs/LoginDialog.cpp ui/dialogs/LoginDialog.h ui/dialogs/MSALoginDialog.cpp @@ -939,6 +941,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/SkinUploadDialog.ui ui/dialogs/ExportInstanceDialog.ui ui/dialogs/IconPickerDialog.ui + ui/dialogs/ImportResourcePackDialog.ui ui/dialogs/MSALoginDialog.ui ui/dialogs/OfflineLoginDialog.ui ui/dialogs/AboutDialog.ui diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.cpp b/launcher/ui/dialogs/ImportResourcePackDialog.cpp new file mode 100644 index 00000000..ef76445c --- /dev/null +++ b/launcher/ui/dialogs/ImportResourcePackDialog.cpp @@ -0,0 +1,61 @@ +#include "ImportResourcePackDialog.h" +#include "ui_ImportResourcePackDialog.h" + +#include +#include + +#include "Application.h" +#include "InstanceList.h" + +#include "ui/instanceview/InstanceDelegate.h" + +ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ImportResourcePackDialog) +{ + ui->setupUi(this); + setWindowModality(Qt::WindowModal); + + auto contentsWidget = ui->instanceView; + contentsWidget->setViewMode(QListView::ListMode); + contentsWidget->setFlow(QListView::LeftToRight); + contentsWidget->setIconSize(QSize(48, 48)); + contentsWidget->setMovement(QListView::Static); + contentsWidget->setResizeMode(QListView::Adjust); + contentsWidget->setSelectionMode(QAbstractItemView::SingleSelection); + contentsWidget->setSpacing(5); + contentsWidget->setWordWrap(false); + contentsWidget->setWrapping(true); + contentsWidget->setUniformItemSizes(true); + contentsWidget->setTextElideMode(Qt::ElideRight); + contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + contentsWidget->setItemDelegate(new ListViewDelegate()); + + contentsWidget->setModel(APPLICATION->instances().get()); + + connect(contentsWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(activated(QModelIndex))); + connect(contentsWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SLOT(selectionChanged(QItemSelection, QItemSelection))); +} + +void ImportResourcePackDialog::activated(QModelIndex index) +{ + selectedInstanceKey = index.data(Qt::UserRole).toString(); + accept(); +} + +void ImportResourcePackDialog::selectionChanged(QItemSelection selected, QItemSelection deselected) +{ + if (selected.empty()) + return; + + QString key = selected.first().indexes().first().data(Qt::UserRole).toString(); + if (!key.isEmpty()) { + selectedInstanceKey = key; + } +} + +ImportResourcePackDialog::~ImportResourcePackDialog() +{ + delete ui; +} diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.h b/launcher/ui/dialogs/ImportResourcePackDialog.h new file mode 100644 index 00000000..b077a811 --- /dev/null +++ b/launcher/ui/dialogs/ImportResourcePackDialog.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace Ui { +class ImportResourcePackDialog; +} + +class ImportResourcePackDialog : public QDialog { + Q_OBJECT + + public: + explicit ImportResourcePackDialog(QWidget* parent = 0); + ~ImportResourcePackDialog(); + QString selectedInstanceKey; + + private: + Ui::ImportResourcePackDialog* ui; + + private slots: + void selectionChanged(QItemSelection, QItemSelection); + void activated(QModelIndex); +}; diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.ui b/launcher/ui/dialogs/ImportResourcePackDialog.ui new file mode 100644 index 00000000..2a1de0f9 --- /dev/null +++ b/launcher/ui/dialogs/ImportResourcePackDialog.ui @@ -0,0 +1,67 @@ + + + ImportResourcePackDialog + + + + 0 + 0 + 676 + 555 + + + + Pick icon + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ImportResourcePackDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ImportResourcePackDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From b1bdc6f745d607af4dc1bb592003a538ee03f058 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 19 Nov 2022 23:38:05 +0800 Subject: [PATCH 131/277] fix resource packs and add support for texture packs Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/Application.cpp | 29 ++++++++++++++++++------- launcher/minecraft/mod/ResourcePack.cpp | 5 +++++ launcher/minecraft/mod/ResourcePack.h | 2 ++ launcher/minecraft/mod/TexturePack.cpp | 5 +++++ launcher/minecraft/mod/TexturePack.h | 2 ++ 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 71cd009a..9258aec4 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -101,6 +101,9 @@ #include #include #include +#include +#include +#include #include "updater/UpdateChecker.h" @@ -920,13 +923,13 @@ bool Application::createSetupWizard() return false; } -bool Application::event(QEvent* event) { +bool Application::event(QEvent* event) +{ #ifdef Q_OS_MACOS if (event->type() == QEvent::ApplicationStateChange) { auto ev = static_cast(event); - if (m_prevAppState == Qt::ApplicationActive - && ev->applicationState() == Qt::ApplicationActive) { + if (m_prevAppState == Qt::ApplicationActive && ev->applicationState() == Qt::ApplicationActive) { emit clickedOnDock(); } m_prevAppState = ev->applicationState(); @@ -936,19 +939,29 @@ bool Application::event(QEvent* event) { if (event->type() == QEvent::FileOpen) { auto ev = static_cast(event); - ResourcePack pack{ QFileInfo(ev->file()) }; + ResourcePack rp{ QFileInfo(ev->file()) }; + TexturePack tp{ QFileInfo(ev->file()) }; - ResourcePackUtils::process(pack); - // + ImportResourcePackDialog dlg(APPLICATION->m_mainWindow); - if (pack.valid()) { - ImportResourcePackDialog dlg(APPLICATION->m_mainWindow); + if (ResourcePackUtils::process(rp) && rp.valid()) { dlg.exec(); + if (dlg.result() == QDialog::Accepted) { + qDebug() << "Selected instance to import resource pack into: " << dlg.selectedInstanceKey; auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); auto instanceButBuffed = std::dynamic_pointer_cast(instance); instanceButBuffed->resourcePackList()->installResource(ev->file()); } + } else if (TexturePackUtils::process(tp) && tp.valid()) { + dlg.exec(); + + if (dlg.result() == QDialog::Accepted) { + qDebug() << "Selected instance to import texture pack into: " << dlg.selectedInstanceKey; + auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); + auto instanceButBuffed = std::dynamic_pointer_cast(instance); + instanceButBuffed->texturePackList()->installResource(ev->file()); + } } else { m_mainWindow->droppedURLs({ ev->url() }); } diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index 3fc10a2f..9f46cd2c 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -114,3 +114,8 @@ bool ResourcePack::applyFilter(QRegularExpression filter) const return Resource::applyFilter(filter); } + +bool ResourcePack::valid() const +{ + return m_pack_format != 0; +} diff --git a/launcher/minecraft/mod/ResourcePack.h b/launcher/minecraft/mod/ResourcePack.h index 03121908..7cb414d8 100644 --- a/launcher/minecraft/mod/ResourcePack.h +++ b/launcher/minecraft/mod/ResourcePack.h @@ -42,6 +42,8 @@ class ResourcePack : public Resource { /** Thread-safe. */ void setImage(QImage new_image); + bool valid() const override; + [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; diff --git a/launcher/minecraft/mod/TexturePack.cpp b/launcher/minecraft/mod/TexturePack.cpp index 796eb69d..99d55584 100644 --- a/launcher/minecraft/mod/TexturePack.cpp +++ b/launcher/minecraft/mod/TexturePack.cpp @@ -62,3 +62,8 @@ QPixmap TexturePack::image(QSize size) TexturePackUtils::process(*this); return image(size); } + +bool TexturePack::valid() const +{ + return m_description != nullptr; +} diff --git a/launcher/minecraft/mod/TexturePack.h b/launcher/minecraft/mod/TexturePack.h index 6aa5e18e..81bd5c69 100644 --- a/launcher/minecraft/mod/TexturePack.h +++ b/launcher/minecraft/mod/TexturePack.h @@ -48,6 +48,8 @@ class TexturePack : public Resource { /** Thread-safe. */ void setImage(QImage new_image); + bool valid() const override; + protected: mutable QMutex m_data_lock; From f527958cb35b976d88e7037bda62762c1ffe6e13 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 19 Nov 2022 23:42:56 +0800 Subject: [PATCH 132/277] fix: check if packFormat is valid for resourcepack parsing Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/minecraft/mod/ResourcePack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index 9f46cd2c..1e9db596 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -117,5 +117,5 @@ bool ResourcePack::applyFilter(QRegularExpression filter) const bool ResourcePack::valid() const { - return m_pack_format != 0; + return s_pack_format_versions.contains(m_pack_format); } From a33b8049238329fa97545c0b2db5bb8c2f7545c6 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 19 Nov 2022 23:47:47 +0800 Subject: [PATCH 133/277] fix: add resource pack format for 1.19.3 Co-authored-by: Rachel Powers <508861+Ryex@users.noreply.github.com> Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/minecraft/mod/ResourcePack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index 1e9db596..ef0370d0 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -15,7 +15,7 @@ static const QMap> s_pack_format_versions = { { 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } }, { 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } }, - { 9, { Version("1.19"), Version("1.19.2") } }, + { 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("1.19.3"), Version("1.19.3") } }, }; void ResourcePack::setPackFormat(int new_format_id) From 1f6b8f9d2bddd73e6cfd9d430ef9e37d1910adff Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 19 Nov 2022 23:59:30 +0800 Subject: [PATCH 134/277] fix instance IDs and resource pack imports Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 +- launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp | 2 +- launcher/ui/dialogs/ImportResourcePackDialog.cpp | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 4f87bc13..d744c535 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -152,7 +152,7 @@ bool LocalResourcePackParseTask::abort() void LocalResourcePackParseTask::executeTask() { - Q_ASSERT(m_resource_pack.valid()); + // Q_ASSERT(m_resource_pack.valid()); if (!ResourcePackUtils::process(m_resource_pack)) return; diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index bf1e308f..f23117ee 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -143,7 +143,7 @@ bool LocalTexturePackParseTask::abort() void LocalTexturePackParseTask::executeTask() { - Q_ASSERT(m_texture_pack.valid()); + // Q_ASSERT(m_texture_pack.valid()); if (!TexturePackUtils::process(m_texture_pack)) return; diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.cpp b/launcher/ui/dialogs/ImportResourcePackDialog.cpp index ef76445c..4fe28540 100644 --- a/launcher/ui/dialogs/ImportResourcePackDialog.cpp +++ b/launcher/ui/dialogs/ImportResourcePackDialog.cpp @@ -7,6 +7,7 @@ #include "Application.h" #include "InstanceList.h" +#include #include "ui/instanceview/InstanceDelegate.h" ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ImportResourcePackDialog) @@ -40,7 +41,7 @@ ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(pa void ImportResourcePackDialog::activated(QModelIndex index) { - selectedInstanceKey = index.data(Qt::UserRole).toString(); + selectedInstanceKey = index.data(InstanceList::InstanceIDRole).toString(); accept(); } @@ -49,7 +50,7 @@ void ImportResourcePackDialog::selectionChanged(QItemSelection selected, QItemSe if (selected.empty()) return; - QString key = selected.first().indexes().first().data(Qt::UserRole).toString(); + QString key = selected.first().indexes().first().data(InstanceList::InstanceIDRole).toString(); if (!key.isEmpty()) { selectedInstanceKey = key; } From a99cd16422bb577028a1caaa6e5bde300ac78372 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:14:16 +0800 Subject: [PATCH 135/277] fix: resource pack import dialog icons and add message Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/ui/dialogs/ImportResourcePackDialog.cpp | 6 +++++- launcher/ui/dialogs/ImportResourcePackDialog.h | 3 +++ launcher/ui/dialogs/ImportResourcePackDialog.ui | 9 ++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.cpp b/launcher/ui/dialogs/ImportResourcePackDialog.cpp index 4fe28540..2b746605 100644 --- a/launcher/ui/dialogs/ImportResourcePackDialog.cpp +++ b/launcher/ui/dialogs/ImportResourcePackDialog.cpp @@ -8,6 +8,7 @@ #include "InstanceList.h" #include +#include "ui/instanceview/InstanceProxyModel.h" #include "ui/instanceview/InstanceDelegate.h" ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ImportResourcePackDialog) @@ -32,7 +33,10 @@ ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(pa contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); contentsWidget->setItemDelegate(new ListViewDelegate()); - contentsWidget->setModel(APPLICATION->instances().get()); + proxyModel = new InstanceProxyModel(this); + proxyModel->setSourceModel(APPLICATION->instances().get()); + proxyModel->sort(0); + contentsWidget->setModel(proxyModel); connect(contentsWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(activated(QModelIndex))); connect(contentsWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.h b/launcher/ui/dialogs/ImportResourcePackDialog.h index b077a811..8356f204 100644 --- a/launcher/ui/dialogs/ImportResourcePackDialog.h +++ b/launcher/ui/dialogs/ImportResourcePackDialog.h @@ -3,6 +3,8 @@ #include #include +#include "ui/instanceview/InstanceProxyModel.h" + namespace Ui { class ImportResourcePackDialog; } @@ -13,6 +15,7 @@ class ImportResourcePackDialog : public QDialog { public: explicit ImportResourcePackDialog(QWidget* parent = 0); ~ImportResourcePackDialog(); + InstanceProxyModel* proxyModel; QString selectedInstanceKey; private: diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.ui b/launcher/ui/dialogs/ImportResourcePackDialog.ui index 2a1de0f9..20cb9177 100644 --- a/launcher/ui/dialogs/ImportResourcePackDialog.ui +++ b/launcher/ui/dialogs/ImportResourcePackDialog.ui @@ -11,9 +11,16 @@ - Pick icon + Choose instance to import + + + + Choose the instance you would like to import this resource pack to. + + + From c27ebde575e4b12aa38da962b18bb261bbe676b4 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sat, 19 Nov 2022 17:14:19 +0100 Subject: [PATCH 136/277] fix(actions): fix cache on flatpak currently there's a [bug](https://github.com/flatpak/flatpak-github-actions/issues/80) on the stable version of the flatpak action which causes the cache key to be wrong. this commit work arounds it Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 20fe66dd..c0e5b50a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -487,6 +487,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout + if: inputs.build_type == 'Debug' uses: actions/checkout@v3 with: submodules: 'true' @@ -524,3 +525,4 @@ jobs: with: bundle: "Prism Launcher.flatpak" manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml + cache-key: flatpak-${{ github.sha }}-x86_64 From d1db7a0e23d90546f6b7ee5d1894fb3c60e41ef7 Mon Sep 17 00:00:00 2001 From: jopejoe1 <34899572+jopejoe1@users.noreply.github.com> Date: Sat, 19 Nov 2022 16:28:27 +0000 Subject: [PATCH 137/277] Fix New Zealand English display Signed-off-by: jopejoe1 <34899572+jopejoe1@users.noreply.github.com> --- launcher/translations/TranslationsModel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 84778d32..38f48296 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -83,6 +83,9 @@ struct Language else if(key == "es_UY") { result = u8"español de Latinoamérica"; } + else if(key == "en_NZ") { + result = u8"New Zealand English"; // No idea why qt translates this to just english and not to New Zealand English + } else if(key == "en@pirate") { result = u8"Tongue of the High Seas"; } From e0e428ce38ff5662089036a6bbf017a3b39f478f Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:28:35 +0800 Subject: [PATCH 138/277] fix: add support for CLI and drag and drop Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/Application.cpp | 35 +-------------------------- launcher/ui/MainWindow.cpp | 48 +++++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 9258aec4..0fbe4ae2 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -66,7 +66,6 @@ #include "ui/setupwizard/PasteWizardPage.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/ImportResourcePackDialog.h" #include "ui/pagedialog/PageDialog.h" @@ -98,12 +97,6 @@ #include "java/JavaUtils.h" #include -#include -#include -#include -#include -#include -#include #include "updater/UpdateChecker.h" @@ -938,33 +931,7 @@ bool Application::event(QEvent* event) if (event->type() == QEvent::FileOpen) { auto ev = static_cast(event); - - ResourcePack rp{ QFileInfo(ev->file()) }; - TexturePack tp{ QFileInfo(ev->file()) }; - - ImportResourcePackDialog dlg(APPLICATION->m_mainWindow); - - if (ResourcePackUtils::process(rp) && rp.valid()) { - dlg.exec(); - - if (dlg.result() == QDialog::Accepted) { - qDebug() << "Selected instance to import resource pack into: " << dlg.selectedInstanceKey; - auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); - auto instanceButBuffed = std::dynamic_pointer_cast(instance); - instanceButBuffed->resourcePackList()->installResource(ev->file()); - } - } else if (TexturePackUtils::process(tp) && tp.valid()) { - dlg.exec(); - - if (dlg.result() == QDialog::Accepted) { - qDebug() << "Selected instance to import texture pack into: " << dlg.selectedInstanceKey; - auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); - auto instanceButBuffed = std::dynamic_pointer_cast(instance); - instanceButBuffed->texturePackList()->installResource(ev->file()); - } - } else { - m_mainWindow->droppedURLs({ ev->url() }); - } + m_mainWindow->droppedURLs({ ev->url() }); } return QApplication::event(event); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 929f2a85..ed61777e 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -106,8 +106,16 @@ #include "ui/dialogs/UpdateDialog.h" #include "ui/dialogs/EditAccountDialog.h" #include "ui/dialogs/ExportInstanceDialog.h" +#include "ui/dialogs/ImportResourcePackDialog.h" #include "ui/themes/ITheme.h" +#include +#include +#include +#include +#include +#include + #include "UpdateController.h" #include "KonamiCode.h" @@ -1794,16 +1802,40 @@ void MainWindow::on_actionAddInstance_triggered() void MainWindow::droppedURLs(QList urls) { - for(auto & url:urls) - { - if(url.isLocalFile()) - { - addInstance(url.toLocalFile()); - } - else - { + for (auto& url : urls) { + if (url.isLocalFile()) { + auto localFileName = url.toLocalFile(); + + ResourcePack rp{ QFileInfo(localFileName) }; + TexturePack tp{ QFileInfo(localFileName) }; + + ImportResourcePackDialog dlg(this); + + if (ResourcePackUtils::process(rp) && rp.valid()) { + dlg.exec(); + + if (dlg.result() == QDialog::Accepted) { + qDebug() << "Selected instance to import resource pack into: " << dlg.selectedInstanceKey; + auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); + auto instanceButBuffed = std::dynamic_pointer_cast(instance); + instanceButBuffed->resourcePackList()->installResource(localFileName); + } + } else if (TexturePackUtils::process(tp) && tp.valid()) { + dlg.exec(); + + if (dlg.result() == QDialog::Accepted) { + qDebug() << "Selected instance to import texture pack into: " << dlg.selectedInstanceKey; + auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); + auto instanceButBuffed = std::dynamic_pointer_cast(instance); + instanceButBuffed->texturePackList()->installResource(localFileName); + } + } else { + addInstance(localFileName); + } + } else { addInstance(url.toString()); } + // Only process one dropped file... break; } From d92ae530d7c585eb859d852ba1877230a82d867e Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sun, 20 Nov 2022 00:31:58 +0800 Subject: [PATCH 139/277] fix: stray include Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/Application.cpp | 1 - launcher/ui/MainWindow.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 0fbe4ae2..c5594b21 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -96,7 +96,6 @@ #include "net/HttpMetaCache.h" #include "java/JavaUtils.h" -#include #include "updater/UpdateChecker.h" diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index ed61777e..98fd79be 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -71,6 +71,7 @@ #include #include +#include #include #include #include From 2367903ac6c6f6778935ed1bbab88fd8342dffa0 Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 19 Nov 2022 11:55:40 -0300 Subject: [PATCH 140/277] refactor: clean up WideBar a bit Signed-off-by: flow --- launcher/ui/widgets/WideBar.cpp | 130 +++++++++++++++----------------- launcher/ui/widgets/WideBar.h | 15 ++-- 2 files changed, 68 insertions(+), 77 deletions(-) diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index 79f1e0c9..ed5c5bc8 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -1,19 +1,21 @@ #include "WideBar.h" #include -#include -class ActionButton : public QToolButton -{ +class ActionButton : public QToolButton { Q_OBJECT -public: - ActionButton(QAction * action, QWidget * parent = 0) : QToolButton(parent), m_action(action) { + public: + ActionButton(QAction* action, QWidget* parent = nullptr) : QToolButton(parent), m_action(action) + { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + connect(action, &QAction::changed, this, &ActionButton::actionChanged); connect(this, &ActionButton::clicked, action, &QAction::trigger); + actionChanged(); }; -private slots: - void actionChanged() { + private slots: + void actionChanged() + { setEnabled(m_action->isEnabled()); setChecked(m_action->isChecked()); setCheckable(m_action->isCheckable()); @@ -23,10 +25,10 @@ private slots: setHidden(!m_action->isVisible()); setFocusPolicy(Qt::NoFocus); } -private: - QAction * m_action; -}; + private: + QAction* m_action; +}; WideBar::WideBar(const QString& title, QWidget* parent) : QToolBar(title, parent) { @@ -40,116 +42,102 @@ WideBar::WideBar(QWidget* parent) : QToolBar(parent) setMovable(false); } -struct WideBar::BarEntry { - enum Type { - None, - Action, - Separator, - Spacer - } type = None; - QAction *qAction = nullptr; - QAction *wideAction = nullptr; -}; - - -WideBar::~WideBar() -{ - for(auto *iter: m_entries) { - delete iter; - } -} - void WideBar::addAction(QAction* action) { - auto entry = new BarEntry(); - entry->qAction = addWidget(new ActionButton(action, this)); - entry->wideAction = action; - entry->type = BarEntry::Action; + BarEntry entry; + entry.bar_action = addWidget(new ActionButton(action, this)); + entry.menu_action = action; + entry.type = BarEntry::Type::Action; + m_entries.push_back(entry); } void WideBar::addSeparator() { - auto entry = new BarEntry(); - entry->qAction = QToolBar::addSeparator(); - entry->type = BarEntry::Separator; + BarEntry entry; + entry.bar_action = QToolBar::addSeparator(); + entry.type = BarEntry::Type::Separator; + m_entries.push_back(entry); } -auto WideBar::getMatching(QAction* act) -> QList::iterator +auto WideBar::getMatching(QAction* act) -> QList::iterator { - auto iter = std::find_if(m_entries.begin(), m_entries.end(), [act](BarEntry * entry) { - return entry->wideAction == act; - }); - + auto iter = std::find_if(m_entries.begin(), m_entries.end(), [act](BarEntry const& entry) { return entry.menu_action == act; }); + return iter; } -void WideBar::insertActionBefore(QAction* before, QAction* action){ +void WideBar::insertActionBefore(QAction* before, QAction* action) +{ auto iter = getMatching(before); - if(iter == m_entries.end()) + if (iter == m_entries.end()) return; - auto entry = new BarEntry(); - entry->qAction = insertWidget((*iter)->qAction, new ActionButton(action, this)); - entry->wideAction = action; - entry->type = BarEntry::Action; + BarEntry entry; + entry.bar_action = insertWidget(iter->bar_action, new ActionButton(action, this)); + entry.menu_action = action; + entry.type = BarEntry::Type::Action; + m_entries.insert(iter, entry); } -void WideBar::insertActionAfter(QAction* after, QAction* action){ +void WideBar::insertActionAfter(QAction* after, QAction* action) +{ auto iter = getMatching(after); - if(iter == m_entries.end()) + if (iter == m_entries.end()) return; - auto entry = new BarEntry(); - entry->qAction = insertWidget((*(iter+1))->qAction, new ActionButton(action, this)); - entry->wideAction = action; - entry->type = BarEntry::Action; + BarEntry entry; + entry.bar_action = insertWidget((iter + 1)->bar_action, new ActionButton(action, this)); + entry.menu_action = action; + entry.type = BarEntry::Type::Action; + m_entries.insert(iter + 1, entry); } void WideBar::insertSpacer(QAction* action) { auto iter = getMatching(action); - if(iter == m_entries.end()) + if (iter == m_entries.end()) return; - QWidget* spacer = new QWidget(); + auto* spacer = new QWidget(); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - auto entry = new BarEntry(); - entry->qAction = insertWidget((*iter)->qAction, spacer); - entry->type = BarEntry::Spacer; + BarEntry entry; + entry.bar_action = insertWidget(iter->bar_action, spacer); + entry.type = BarEntry::Type::Spacer; m_entries.insert(iter, entry); } void WideBar::insertSeparator(QAction* before) { auto iter = getMatching(before); - if(iter == m_entries.end()) + if (iter == m_entries.end()) return; - auto entry = new BarEntry(); - entry->qAction = QToolBar::insertSeparator(before); - entry->type = BarEntry::Separator; + BarEntry entry; + entry.bar_action = QToolBar::insertSeparator(before); + entry.type = BarEntry::Type::Separator; + m_entries.insert(iter, entry); } -QMenu * WideBar::createContextMenu(QWidget *parent, const QString & title) +QMenu* WideBar::createContextMenu(QWidget* parent, const QString& title) { - QMenu *contextMenu = new QMenu(title, parent); - for(auto & item: m_entries) { - switch(item->type) { + auto* contextMenu = new QMenu(title, parent); + for (auto& item : m_entries) { + switch (item.type) { default: - case BarEntry::None: + case BarEntry::Type::None: break; - case BarEntry::Separator: - case BarEntry::Spacer: + case BarEntry::Type::Separator: + case BarEntry::Type::Spacer: contextMenu->addSeparator(); break; - case BarEntry::Action: - contextMenu->addAction(item->wideAction); + case BarEntry::Type::Action: + contextMenu->addAction(item.menu_action); break; } } diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h index 8ff62ef2..4a714c80 100644 --- a/launcher/ui/widgets/WideBar.h +++ b/launcher/ui/widgets/WideBar.h @@ -2,17 +2,16 @@ #include #include +#include #include -class QMenu; - class WideBar : public QToolBar { Q_OBJECT public: explicit WideBar(const QString& title, QWidget* parent = nullptr); explicit WideBar(QWidget* parent = nullptr); - virtual ~WideBar(); + ~WideBar() override = default; void addAction(QAction* action); void addSeparator(); @@ -25,10 +24,14 @@ class WideBar : public QToolBar { QMenu* createContextMenu(QWidget* parent = nullptr, const QString& title = QString()); private: - struct BarEntry; + struct BarEntry { + enum class Type { None, Action, Separator, Spacer } type = Type::None; + QAction* bar_action = nullptr; + QAction* menu_action = nullptr; + }; - auto getMatching(QAction* act) -> QList::iterator; + auto getMatching(QAction* act) -> QList::iterator; private: - QList m_entries; + QList m_entries; }; From 6e1639551bbe98b32e9abef2d816e8abe01789e4 Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 19 Nov 2022 13:39:43 -0300 Subject: [PATCH 141/277] feat(WideBar): allow hiding buttons with right-click Signed-off-by: flow --- launcher/ui/widgets/WideBar.cpp | 58 ++++++++++++++++++++++++++++++++- launcher/ui/widgets/WideBar.h | 7 ++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index ed5c5bc8..ed7dc5fa 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -1,4 +1,6 @@ #include "WideBar.h" + +#include #include class ActionButton : public QToolButton { @@ -13,7 +15,7 @@ class ActionButton : public QToolButton { actionChanged(); }; - private slots: + public slots: void actionChanged() { setEnabled(m_action->isEnabled()); @@ -34,12 +36,16 @@ WideBar::WideBar(const QString& title, QWidget* parent) : QToolBar(title, parent { setFloatable(false); setMovable(false); + + m_bar_menu = std::make_unique(this); } WideBar::WideBar(QWidget* parent) : QToolBar(parent) { setFloatable(false); setMovable(false); + + m_bar_menu = std::make_unique(this); } void WideBar::addAction(QAction* action) @@ -50,6 +56,8 @@ void WideBar::addAction(QAction* action) entry.type = BarEntry::Type::Action; m_entries.push_back(entry); + + m_menu_state = MenuState::Dirty; } void WideBar::addSeparator() @@ -80,6 +88,8 @@ void WideBar::insertActionBefore(QAction* before, QAction* action) entry.type = BarEntry::Type::Action; m_entries.insert(iter, entry); + + m_menu_state = MenuState::Dirty; } void WideBar::insertActionAfter(QAction* after, QAction* action) @@ -94,6 +104,8 @@ void WideBar::insertActionAfter(QAction* after, QAction* action) entry.type = BarEntry::Type::Action; m_entries.insert(iter + 1, entry); + + m_menu_state = MenuState::Dirty; } void WideBar::insertSpacer(QAction* action) @@ -144,4 +156,48 @@ QMenu* WideBar::createContextMenu(QWidget* parent, const QString& title) return contextMenu; } +static void copyAction(QAction* from, QAction* to) +{ + Q_ASSERT(from); + Q_ASSERT(to); + + to->setText(from->text()); + to->setIcon(from->icon()); + to->setToolTip(from->toolTip()); +} + +void WideBar::contextMenuEvent(QContextMenuEvent* event) +{ + if (m_menu_state == MenuState::Dirty) { + for (auto* old_action : m_bar_menu->actions()) + old_action->deleteLater(); + + m_bar_menu->clear(); + + for (auto& entry : m_entries) { + if (entry.type != BarEntry::Type::Action) + continue; + + auto act = new QAction(); + copyAction(entry.menu_action, act); + + act->setCheckable(true); + act->setChecked(entry.bar_action->isVisible()); + + connect(act, &QAction::toggled, entry.bar_action, [this, &entry](bool toggled){ + entry.bar_action->setVisible(toggled); + + // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible. + static_cast(widgetForAction(entry.bar_action))->actionChanged(); + }); + + m_bar_menu->addAction(act); + } + + m_menu_state = MenuState::Fresh; + } + + m_bar_menu->popup(event->globalPos()); +} + #include "WideBar.moc" diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h index 4a714c80..8421eaf4 100644 --- a/launcher/ui/widgets/WideBar.h +++ b/launcher/ui/widgets/WideBar.h @@ -5,6 +5,8 @@ #include #include +#include + class WideBar : public QToolBar { Q_OBJECT @@ -22,6 +24,7 @@ class WideBar : public QToolBar { void insertActionAfter(QAction* after, QAction* action); QMenu* createContextMenu(QWidget* parent = nullptr, const QString& title = QString()); + void contextMenuEvent(QContextMenuEvent*) override; private: struct BarEntry { @@ -34,4 +37,8 @@ class WideBar : public QToolBar { private: QList m_entries; + + // Menu to toggle visibility from buttons in the bar + std::unique_ptr m_bar_menu = nullptr; + enum class MenuState { Fresh, Dirty } m_menu_state = MenuState::Dirty; }; From 479843f56b42d7044d3d02278a9cabc2c24e147a Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 19 Nov 2022 17:10:43 -0300 Subject: [PATCH 142/277] feat(WideBar): allow loading/unloading visibility via a byte array I really wanted to use a QBitArray :c Signed-off-by: flow --- launcher/ui/widgets/WideBar.cpp | 30 ++++++++++++++++++++++++++++++ launcher/ui/widgets/WideBar.h | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index ed7dc5fa..2ad2caec 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -200,4 +200,34 @@ void WideBar::contextMenuEvent(QContextMenuEvent* event) m_bar_menu->popup(event->globalPos()); } +[[nodiscard]] QByteArray WideBar::getVisibilityState() const +{ + QByteArray state; + + for (auto const& entry : m_entries) { + if (entry.type != BarEntry::Type::Action) + continue; + + state.append(entry.bar_action->isVisible() ? '1' : '0'); + } + + return state; +} + +void WideBar::setVisibilityState(QByteArray&& state) +{ + qsizetype i = 0; + for (auto& entry : m_entries) { + if (entry.type != BarEntry::Type::Action) + continue; + if (i == state.size()) + break; + + entry.bar_action->setVisible(state.at(i++) == '1'); + + // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible. + static_cast(widgetForAction(entry.bar_action))->actionChanged(); + } +} + #include "WideBar.moc" diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h index 8421eaf4..0d60f8a4 100644 --- a/launcher/ui/widgets/WideBar.h +++ b/launcher/ui/widgets/WideBar.h @@ -26,6 +26,12 @@ class WideBar : public QToolBar { QMenu* createContextMenu(QWidget* parent = nullptr, const QString& title = QString()); void contextMenuEvent(QContextMenuEvent*) override; + // Ideally we would use a QBitArray for this, but it doesn't support string conversion, + // so using it in settings is very messy. + + [[nodiscard]] QByteArray getVisibilityState() const; + void setVisibilityState(QByteArray&&); + private: struct BarEntry { enum class Type { None, Action, Separator, Spacer } type = Type::None; From 2d69d63efe12a1cfaf391a59cb6b7630a436410e Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 19 Nov 2022 17:12:31 -0300 Subject: [PATCH 143/277] feat(InstancePages): save/load wide bar visibility settings Signed-off-by: flow --- .../ui/pages/instance/ExternalResourcesPage.cpp | 10 ++++++++++ .../ui/pages/instance/ExternalResourcesPage.h | 3 +++ launcher/ui/pages/instance/ScreenshotsPage.cpp | 13 +++++++++++++ launcher/ui/pages/instance/ScreenshotsPage.h | 7 ++++++- launcher/ui/pages/instance/ServersPage.cpp | 10 ++++++++++ launcher/ui/pages/instance/ServersPage.h | 4 ++++ launcher/ui/pages/instance/VersionPage.cpp | 15 +++++++++++++++ launcher/ui/pages/instance/VersionPage.h | 5 +++++ launcher/ui/pages/instance/WorldListPage.cpp | 10 ++++++++++ launcher/ui/pages/instance/WorldListPage.h | 4 ++++ 10 files changed, 80 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index b6c873cc..381fa30c 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -70,11 +70,21 @@ void ExternalResourcesPage::ShowContextMenu(const QPoint& pos) void ExternalResourcesPage::openedImpl() { m_model->startWatching(); + + auto const setting_name = QString("WideBarVisibility_%1").arg(id()); + if (!APPLICATION->settings()->contains(setting_name)) + m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name); + else + m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); + + ui->actionsToolbar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); } void ExternalResourcesPage::closedImpl() { m_model->stopWatching(); + + m_wide_bar_setting->set(ui->actionsToolbar->getVisibilityState()); } void ExternalResourcesPage::retranslate() diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index 8e352cef..b816e742 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -4,6 +4,7 @@ #include #include "Application.h" +#include "settings/Setting.h" #include "minecraft/MinecraftInstance.h" #include "ui/pages/BasePage.h" @@ -71,4 +72,6 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { QString m_viewFilter; bool m_controlsEnabled = true; + + std::shared_ptr m_wide_bar_setting = nullptr; }; diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index c97253e4..0092aef3 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -537,6 +537,19 @@ void ScreenshotsPage::openedImpl() ui->listView->setModel(nullptr); } } + + auto const setting_name = QString("WideBarVisibility_%1").arg(id()); + if (!APPLICATION->settings()->contains(setting_name)) + m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name); + else + m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); + + ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); +} + +void ScreenshotsPage::closedImpl() +{ + m_wide_bar_setting->set(ui->toolBar->getVisibilityState()); } #include "ScreenshotsPage.moc" diff --git a/launcher/ui/pages/instance/ScreenshotsPage.h b/launcher/ui/pages/instance/ScreenshotsPage.h index c22706af..2eb0de04 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.h +++ b/launcher/ui/pages/instance/ScreenshotsPage.h @@ -40,6 +40,8 @@ #include "ui/pages/BasePage.h" #include +#include "settings/Setting.h" + class QFileSystemModel; class QIdentityProxyModel; namespace Ui @@ -59,7 +61,8 @@ public: explicit ScreenshotsPage(QString path, QWidget *parent = 0); virtual ~ScreenshotsPage(); - virtual void openedImpl() override; + void openedImpl() override; + void closedImpl() override; enum { @@ -110,4 +113,6 @@ private: QString m_folder; bool m_valid = false; bool m_uploadActive = false; + + std::shared_ptr m_wide_bar_setting = nullptr; }; diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index d64bcb76..a625e20b 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -765,11 +765,21 @@ void ServersPage::updateState() void ServersPage::openedImpl() { m_model->observe(); + + auto const setting_name = QString("WideBarVisibility_%1").arg(id()); + if (!APPLICATION->settings()->contains(setting_name)) + m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name); + else + m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); + + ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); } void ServersPage::closedImpl() { m_model->unobserve(); + + m_wide_bar_setting->set(ui->toolBar->getVisibilityState()); } void ServersPage::on_actionAdd_triggered() diff --git a/launcher/ui/pages/instance/ServersPage.h b/launcher/ui/pages/instance/ServersPage.h index 37399d49..548d4d1b 100644 --- a/launcher/ui/pages/instance/ServersPage.h +++ b/launcher/ui/pages/instance/ServersPage.h @@ -42,6 +42,8 @@ #include "ui/pages/BasePage.h" #include +#include "settings/Setting.h" + namespace Ui { class ServersPage; @@ -112,5 +114,7 @@ private: // data Ui::ServersPage *ui = nullptr; ServersModel * m_model = nullptr; InstancePtr m_inst = nullptr; + + std::shared_ptr m_wide_bar_setting = nullptr; }; diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index a021c633..84052f37 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -125,6 +125,21 @@ void VersionPage::retranslate() ui->retranslateUi(this); } +void VersionPage::openedImpl() +{ + auto const setting_name = QString("WideBarVisibility_%1").arg(id()); + if (!APPLICATION->settings()->contains(setting_name)) + m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name); + else + m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); + + ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); +} +void VersionPage::closedImpl() +{ + m_wide_bar_setting->set(ui->toolBar->getVisibilityState()); +} + QMenu * VersionPage::createPopupMenu() { QMenu* filteredMenu = QMainWindow::createPopupMenu(); diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 979311fc..9996e776 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -69,6 +69,9 @@ public: virtual bool shouldDisplay() const override; void retranslate() override; + void openedImpl() override; + void closedImpl() override; + private slots: void on_actionChange_version_triggered(); void on_actionInstall_Forge_triggered(); @@ -114,6 +117,8 @@ private: int currentIdx = 0; bool controlsEnabled = false; + std::shared_ptr m_wide_bar_setting = nullptr; + public slots: void versionCurrent(const QModelIndex ¤t, const QModelIndex &previous); diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 85cc01ff..93458ce4 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -113,11 +113,21 @@ WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr worl void WorldListPage::openedImpl() { m_worlds->startWatching(); + + auto const setting_name = QString("WideBarVisibility_%1").arg(id()); + if (!APPLICATION->settings()->contains(setting_name)) + m_wide_bar_setting = APPLICATION->settings()->registerSetting(setting_name); + else + m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); + + ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); } void WorldListPage::closedImpl() { m_worlds->stopWatching(); + + m_wide_bar_setting->set(ui->toolBar->getVisibilityState()); } WorldListPage::~WorldListPage() diff --git a/launcher/ui/pages/instance/WorldListPage.h b/launcher/ui/pages/instance/WorldListPage.h index 1dc9e53e..925521be 100644 --- a/launcher/ui/pages/instance/WorldListPage.h +++ b/launcher/ui/pages/instance/WorldListPage.h @@ -42,6 +42,8 @@ #include #include +#include "settings/Setting.h" + class WorldList; namespace Ui { @@ -102,6 +104,8 @@ private: unique_qobject_ptr m_mceditProcess; bool m_mceditStarting = false; + std::shared_ptr m_wide_bar_setting = nullptr; + private slots: void on_actionCopy_Seed_triggered(); void on_actionMCEdit_triggered(); From bb7a321c6ed91709ff208efbbef19e05d7dfb220 Mon Sep 17 00:00:00 2001 From: Tayou Date: Sun, 20 Nov 2022 02:51:12 +0100 Subject: [PATCH 144/277] add breeze themes Signed-off-by: Tayou --- launcher/CMakeLists.txt | 4 ++ launcher/main.cpp | 2 + .../resources/breeze_dark/breeze_dark.qrc | 43 ++++++++++++++ launcher/resources/breeze_dark/index.theme | 11 ++++ .../resources/breeze_dark/scalable/about.svg | 12 ++++ .../breeze_dark/scalable/accounts.svg | 17 ++++++ .../resources/breeze_dark/scalable/bug.svg | 13 +++++ .../breeze_dark/scalable/centralmods.svg | 1 + .../breeze_dark/scalable/checkupdate.svg | 14 +++++ .../resources/breeze_dark/scalable/copy.svg | 11 ++++ .../breeze_dark/scalable/coremods.svg | 1 + .../breeze_dark/scalable/custom-commands.svg | 13 +++++ .../resources/breeze_dark/scalable/delete.svg | 13 +++++ .../breeze_dark/scalable/discord.svg | 1 + .../resources/breeze_dark/scalable/export.svg | 11 ++++ .../breeze_dark/scalable/externaltools.svg | 13 +++++ .../resources/breeze_dark/scalable/help.svg | 13 +++++ .../scalable/instance-settings.svg | 13 +++++ .../breeze_dark/scalable/jarmods.svg | 1 + .../resources/breeze_dark/scalable/java.svg | 10 ++++ .../breeze_dark/scalable/language.svg | 13 +++++ .../resources/breeze_dark/scalable/launch.svg | 8 +++ .../breeze_dark/scalable/launcher.svg | 57 +++++++++++++++++++ .../breeze_dark/scalable/loadermods.svg | 13 +++++ .../resources/breeze_dark/scalable/log.svg | 13 +++++ .../resources/breeze_dark/scalable/matrix.svg | 9 +++ .../breeze_dark/scalable/minecraft.svg | 13 +++++ .../resources/breeze_dark/scalable/new.svg | 18 ++++++ .../resources/breeze_dark/scalable/news.svg | 13 +++++ .../resources/breeze_dark/scalable/notes.svg | 13 +++++ .../breeze_dark/scalable/patreon.svg | 3 + .../resources/breeze_dark/scalable/proxy.svg | 14 +++++ .../breeze_dark/scalable/reddit-alien.svg | 3 + .../breeze_dark/scalable/refresh.svg | 8 +++ .../resources/breeze_dark/scalable/rename.svg | 13 +++++ .../breeze_dark/scalable/resourcepacks.svg | 11 ++++ .../breeze_dark/scalable/screenshots.svg | 13 +++++ .../breeze_dark/scalable/settings.svg | 17 ++++++ .../breeze_dark/scalable/shaderpacks.svg | 13 +++++ .../breeze_dark/scalable/status-bad.svg | 9 +++ .../breeze_dark/scalable/status-good.svg | 10 ++++ .../breeze_dark/scalable/status-yellow.svg | 9 +++ .../resources/breeze_dark/scalable/tag.svg | 17 ++++++ .../breeze_dark/scalable/viewfolder.svg | 13 +++++ .../resources/breeze_dark/scalable/worlds.svg | 16 ++++++ .../resources/breeze_light/breeze_light.qrc | 43 ++++++++++++++ launcher/resources/breeze_light/index.theme | 11 ++++ .../resources/breeze_light/scalable/about.svg | 12 ++++ .../breeze_light/scalable/accounts.svg | 17 ++++++ .../resources/breeze_light/scalable/bug.svg | 13 +++++ .../breeze_light/scalable/centralmods.svg | 1 + .../breeze_light/scalable/checkupdate.svg | 14 +++++ .../resources/breeze_light/scalable/copy.svg | 11 ++++ .../breeze_light/scalable/coremods.svg | 1 + .../breeze_light/scalable/custom-commands.svg | 13 +++++ .../breeze_light/scalable/delete.svg | 13 +++++ .../breeze_light/scalable/discord.svg | 1 + .../breeze_light/scalable/export.svg | 11 ++++ .../breeze_light/scalable/externaltools.svg | 13 +++++ .../resources/breeze_light/scalable/help.svg | 13 +++++ .../scalable/instance-settings.svg | 13 +++++ .../breeze_light/scalable/jarmods.svg | 1 + .../resources/breeze_light/scalable/java.svg | 10 ++++ .../breeze_light/scalable/language.svg | 13 +++++ .../breeze_light/scalable/launch.svg | 8 +++ .../breeze_light/scalable/loadermods.svg | 13 +++++ .../resources/breeze_light/scalable/log.svg | 13 +++++ .../breeze_light/scalable/matrix.svg | 9 +++ .../breeze_light/scalable/minecraft.svg | 13 +++++ .../resources/breeze_light/scalable/new.svg | 18 ++++++ .../resources/breeze_light/scalable/news.svg | 13 +++++ .../resources/breeze_light/scalable/notes.svg | 13 +++++ .../breeze_light/scalable/patreon.svg | 3 + .../resources/breeze_light/scalable/proxy.svg | 14 +++++ .../breeze_light/scalable/reddit-alien.svg | 3 + .../breeze_light/scalable/refresh.svg | 8 +++ .../breeze_light/scalable/rename.svg | 13 +++++ .../breeze_light/scalable/resourcepacks.svg | 11 ++++ .../breeze_light/scalable/screenshots.svg | 13 +++++ .../breeze_light/scalable/settings.svg | 17 ++++++ .../breeze_light/scalable/shaderpacks.svg | 13 +++++ .../breeze_light/scalable/status-bad.svg | 9 +++ .../breeze_light/scalable/status-good.svg | 10 ++++ .../breeze_light/scalable/status-yellow.svg | 9 +++ .../resources/breeze_light/scalable/tag.svg | 17 ++++++ .../breeze_light/scalable/viewfolder.svg | 13 +++++ .../breeze_light/scalable/worlds.svg | 16 ++++++ launcher/ui/pages/global/LauncherPage.cpp | 29 ++++++++-- launcher/ui/pages/global/LauncherPage.ui | 10 ++++ 89 files changed, 1082 insertions(+), 6 deletions(-) create mode 100644 launcher/resources/breeze_dark/breeze_dark.qrc create mode 100644 launcher/resources/breeze_dark/index.theme create mode 100644 launcher/resources/breeze_dark/scalable/about.svg create mode 100644 launcher/resources/breeze_dark/scalable/accounts.svg create mode 100644 launcher/resources/breeze_dark/scalable/bug.svg create mode 100644 launcher/resources/breeze_dark/scalable/centralmods.svg create mode 100644 launcher/resources/breeze_dark/scalable/checkupdate.svg create mode 100644 launcher/resources/breeze_dark/scalable/copy.svg create mode 100644 launcher/resources/breeze_dark/scalable/coremods.svg create mode 100644 launcher/resources/breeze_dark/scalable/custom-commands.svg create mode 100644 launcher/resources/breeze_dark/scalable/delete.svg create mode 100644 launcher/resources/breeze_dark/scalable/discord.svg create mode 100644 launcher/resources/breeze_dark/scalable/export.svg create mode 100644 launcher/resources/breeze_dark/scalable/externaltools.svg create mode 100644 launcher/resources/breeze_dark/scalable/help.svg create mode 100644 launcher/resources/breeze_dark/scalable/instance-settings.svg create mode 100644 launcher/resources/breeze_dark/scalable/jarmods.svg create mode 100644 launcher/resources/breeze_dark/scalable/java.svg create mode 100644 launcher/resources/breeze_dark/scalable/language.svg create mode 100644 launcher/resources/breeze_dark/scalable/launch.svg create mode 100644 launcher/resources/breeze_dark/scalable/launcher.svg create mode 100644 launcher/resources/breeze_dark/scalable/loadermods.svg create mode 100644 launcher/resources/breeze_dark/scalable/log.svg create mode 100644 launcher/resources/breeze_dark/scalable/matrix.svg create mode 100644 launcher/resources/breeze_dark/scalable/minecraft.svg create mode 100644 launcher/resources/breeze_dark/scalable/new.svg create mode 100644 launcher/resources/breeze_dark/scalable/news.svg create mode 100644 launcher/resources/breeze_dark/scalable/notes.svg create mode 100644 launcher/resources/breeze_dark/scalable/patreon.svg create mode 100644 launcher/resources/breeze_dark/scalable/proxy.svg create mode 100644 launcher/resources/breeze_dark/scalable/reddit-alien.svg create mode 100644 launcher/resources/breeze_dark/scalable/refresh.svg create mode 100644 launcher/resources/breeze_dark/scalable/rename.svg create mode 100644 launcher/resources/breeze_dark/scalable/resourcepacks.svg create mode 100644 launcher/resources/breeze_dark/scalable/screenshots.svg create mode 100644 launcher/resources/breeze_dark/scalable/settings.svg create mode 100644 launcher/resources/breeze_dark/scalable/shaderpacks.svg create mode 100644 launcher/resources/breeze_dark/scalable/status-bad.svg create mode 100644 launcher/resources/breeze_dark/scalable/status-good.svg create mode 100644 launcher/resources/breeze_dark/scalable/status-yellow.svg create mode 100644 launcher/resources/breeze_dark/scalable/tag.svg create mode 100644 launcher/resources/breeze_dark/scalable/viewfolder.svg create mode 100644 launcher/resources/breeze_dark/scalable/worlds.svg create mode 100644 launcher/resources/breeze_light/breeze_light.qrc create mode 100644 launcher/resources/breeze_light/index.theme create mode 100644 launcher/resources/breeze_light/scalable/about.svg create mode 100644 launcher/resources/breeze_light/scalable/accounts.svg create mode 100644 launcher/resources/breeze_light/scalable/bug.svg create mode 100644 launcher/resources/breeze_light/scalable/centralmods.svg create mode 100644 launcher/resources/breeze_light/scalable/checkupdate.svg create mode 100644 launcher/resources/breeze_light/scalable/copy.svg create mode 100644 launcher/resources/breeze_light/scalable/coremods.svg create mode 100644 launcher/resources/breeze_light/scalable/custom-commands.svg create mode 100644 launcher/resources/breeze_light/scalable/delete.svg create mode 100644 launcher/resources/breeze_light/scalable/discord.svg create mode 100644 launcher/resources/breeze_light/scalable/export.svg create mode 100644 launcher/resources/breeze_light/scalable/externaltools.svg create mode 100644 launcher/resources/breeze_light/scalable/help.svg create mode 100644 launcher/resources/breeze_light/scalable/instance-settings.svg create mode 100644 launcher/resources/breeze_light/scalable/jarmods.svg create mode 100644 launcher/resources/breeze_light/scalable/java.svg create mode 100644 launcher/resources/breeze_light/scalable/language.svg create mode 100644 launcher/resources/breeze_light/scalable/launch.svg create mode 100644 launcher/resources/breeze_light/scalable/loadermods.svg create mode 100644 launcher/resources/breeze_light/scalable/log.svg create mode 100644 launcher/resources/breeze_light/scalable/matrix.svg create mode 100644 launcher/resources/breeze_light/scalable/minecraft.svg create mode 100644 launcher/resources/breeze_light/scalable/new.svg create mode 100644 launcher/resources/breeze_light/scalable/news.svg create mode 100644 launcher/resources/breeze_light/scalable/notes.svg create mode 100644 launcher/resources/breeze_light/scalable/patreon.svg create mode 100644 launcher/resources/breeze_light/scalable/proxy.svg create mode 100644 launcher/resources/breeze_light/scalable/reddit-alien.svg create mode 100644 launcher/resources/breeze_light/scalable/refresh.svg create mode 100644 launcher/resources/breeze_light/scalable/rename.svg create mode 100644 launcher/resources/breeze_light/scalable/resourcepacks.svg create mode 100644 launcher/resources/breeze_light/scalable/screenshots.svg create mode 100644 launcher/resources/breeze_light/scalable/settings.svg create mode 100644 launcher/resources/breeze_light/scalable/shaderpacks.svg create mode 100644 launcher/resources/breeze_light/scalable/status-bad.svg create mode 100644 launcher/resources/breeze_light/scalable/status-good.svg create mode 100644 launcher/resources/breeze_light/scalable/status-yellow.svg create mode 100644 launcher/resources/breeze_light/scalable/tag.svg create mode 100644 launcher/resources/breeze_light/scalable/viewfolder.svg create mode 100644 launcher/resources/breeze_light/scalable/worlds.svg diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 8db93429..a92a235d 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -598,6 +598,8 @@ SET(LAUNCHER_SOURCES resources/pe_light/pe_light.qrc resources/pe_colored/pe_colored.qrc resources/pe_blue/pe_blue.qrc + resources/breeze_dark/breeze_dark.qrc + resources/breeze_light/breeze_light.qrc resources/OSX/OSX.qrc resources/iOS/iOS.qrc resources/flat/flat.qrc @@ -957,6 +959,8 @@ qt_add_resources(LAUNCHER_RESOURCES resources/pe_light/pe_light.qrc resources/pe_colored/pe_colored.qrc resources/pe_blue/pe_blue.qrc + resources/breeze_dark/breeze_dark.qrc + resources/breeze_light/breeze_light.qrc resources/OSX/OSX.qrc resources/iOS/iOS.qrc resources/flat/flat.qrc diff --git a/launcher/main.cpp b/launcher/main.cpp index df596449..b63f8bfd 100644 --- a/launcher/main.cpp +++ b/launcher/main.cpp @@ -81,6 +81,8 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(pe_light); Q_INIT_RESOURCE(pe_blue); Q_INIT_RESOURCE(pe_colored); + Q_INIT_RESOURCE(breeze_dark); + Q_INIT_RESOURCE(breeze_light); Q_INIT_RESOURCE(OSX); Q_INIT_RESOURCE(iOS); Q_INIT_RESOURCE(flat); diff --git a/launcher/resources/breeze_dark/breeze_dark.qrc b/launcher/resources/breeze_dark/breeze_dark.qrc new file mode 100644 index 00000000..4d7a69b2 --- /dev/null +++ b/launcher/resources/breeze_dark/breeze_dark.qrc @@ -0,0 +1,43 @@ + + + index.theme + scalable/about.svg + scalable/accounts.svg + scalable/bug.svg + scalable/centralmods.svg + scalable/checkupdate.svg + scalable/copy.svg + scalable/coremods.svg + scalable/custom-commands.svg + scalable/discord.svg + scalable/externaltools.svg + scalable/help.svg + scalable/instance-settings.svg + scalable/jarmods.svg + scalable/java.svg + scalable/language.svg + scalable/loadermods.svg + scalable/log.svg + scalable/minecraft.svg + scalable/new.svg + scalable/news.svg + scalable/notes.svg + scalable/proxy.svg + scalable/reddit-alien.svg + scalable/refresh.svg + scalable/resourcepacks.svg + scalable/shaderpacks.svg + scalable/screenshots.svg + scalable/settings.svg + scalable/status-bad.svg + scalable/status-good.svg + scalable/status-yellow.svg + scalable/viewfolder.svg + scalable/worlds.svg + scalable/delete.svg + scalable/tag.svg + scalable/export.svg + scalable/rename.svg + scalable/launch.svg + + diff --git a/launcher/resources/breeze_dark/index.theme b/launcher/resources/breeze_dark/index.theme new file mode 100644 index 00000000..f9f6f4dc --- /dev/null +++ b/launcher/resources/breeze_dark/index.theme @@ -0,0 +1,11 @@ +[Icon Theme] +Name=Breeze Dark +Comment=Breeze Dark Icons +Inherits=multimc +Directories=scalable + +[scalable] +Size=48 +Type=Scalable +MinSize=16 +MaxSize=256 diff --git a/launcher/resources/breeze_dark/scalable/about.svg b/launcher/resources/breeze_dark/scalable/about.svg new file mode 100644 index 00000000..856d1b2b --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/about.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/accounts.svg b/launcher/resources/breeze_dark/scalable/accounts.svg new file mode 100644 index 00000000..fbb51959 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/accounts.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/bug.svg b/launcher/resources/breeze_dark/scalable/bug.svg new file mode 100644 index 00000000..6ddf482f --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/bug.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/centralmods.svg b/launcher/resources/breeze_dark/scalable/centralmods.svg new file mode 100644 index 00000000..4035e51c --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/centralmods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_dark/scalable/checkupdate.svg b/launcher/resources/breeze_dark/scalable/checkupdate.svg new file mode 100644 index 00000000..cc5dfc16 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/checkupdate.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/copy.svg b/launcher/resources/breeze_dark/scalable/copy.svg new file mode 100644 index 00000000..fe4a36ac --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/copy.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/coremods.svg b/launcher/resources/breeze_dark/scalable/coremods.svg new file mode 100644 index 00000000..ec4ecea8 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/coremods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_dark/scalable/custom-commands.svg b/launcher/resources/breeze_dark/scalable/custom-commands.svg new file mode 100644 index 00000000..44efd39e --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/custom-commands.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/delete.svg b/launcher/resources/breeze_dark/scalable/delete.svg new file mode 100644 index 00000000..c7074585 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/delete.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/discord.svg b/launcher/resources/breeze_dark/scalable/discord.svg new file mode 100644 index 00000000..22ee27ba --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_dark/scalable/export.svg b/launcher/resources/breeze_dark/scalable/export.svg new file mode 100644 index 00000000..b1fe39d1 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/export.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/externaltools.svg b/launcher/resources/breeze_dark/scalable/externaltools.svg new file mode 100644 index 00000000..dd19fb90 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/externaltools.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/help.svg b/launcher/resources/breeze_dark/scalable/help.svg new file mode 100644 index 00000000..b273a8bc --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/help.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/instance-settings.svg b/launcher/resources/breeze_dark/scalable/instance-settings.svg new file mode 100644 index 00000000..c5f0504b --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/instance-settings.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/jarmods.svg b/launcher/resources/breeze_dark/scalable/jarmods.svg new file mode 100644 index 00000000..49a45d36 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/jarmods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_dark/scalable/java.svg b/launcher/resources/breeze_dark/scalable/java.svg new file mode 100644 index 00000000..7149981c --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/java.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/language.svg b/launcher/resources/breeze_dark/scalable/language.svg new file mode 100644 index 00000000..239cdf94 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/language.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/launch.svg b/launcher/resources/breeze_dark/scalable/launch.svg new file mode 100644 index 00000000..25c5fabc --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/launch.svg @@ -0,0 +1,8 @@ + + + + diff --git a/launcher/resources/breeze_dark/scalable/launcher.svg b/launcher/resources/breeze_dark/scalable/launcher.svg new file mode 100644 index 00000000..aeee8433 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/launcher.svg @@ -0,0 +1,57 @@ + + + + Prism Launcher Logo + + + + + + + + + + + + + + + + + + + + + + + Prism Launcher Logo + 19/10/2022 + + + Prism Launcher + + + + + AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke + + + https://github.com/PrismLauncher/PrismLauncher + + + Prism Launcher + + + + + + + + + + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/loadermods.svg b/launcher/resources/breeze_dark/scalable/loadermods.svg new file mode 100644 index 00000000..7bd87188 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/loadermods.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/log.svg b/launcher/resources/breeze_dark/scalable/log.svg new file mode 100644 index 00000000..fcd83c4d --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/log.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/matrix.svg b/launcher/resources/breeze_dark/scalable/matrix.svg new file mode 100644 index 00000000..214f5708 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/matrix.svg @@ -0,0 +1,9 @@ + + + Matrix (protocol) logo + + + + + + \ No newline at end of file diff --git a/launcher/resources/breeze_dark/scalable/minecraft.svg b/launcher/resources/breeze_dark/scalable/minecraft.svg new file mode 100644 index 00000000..1d8d0167 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/minecraft.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/new.svg b/launcher/resources/breeze_dark/scalable/new.svg new file mode 100644 index 00000000..9ee910e7 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/new.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/news.svg b/launcher/resources/breeze_dark/scalable/news.svg new file mode 100644 index 00000000..a2ff0c8d --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/news.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/notes.svg b/launcher/resources/breeze_dark/scalable/notes.svg new file mode 100644 index 00000000..6452d3c8 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/notes.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/patreon.svg b/launcher/resources/breeze_dark/scalable/patreon.svg new file mode 100644 index 00000000..7f98dd13 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/patreon.svg @@ -0,0 +1,3 @@ + + + diff --git a/launcher/resources/breeze_dark/scalable/proxy.svg b/launcher/resources/breeze_dark/scalable/proxy.svg new file mode 100644 index 00000000..c6efb171 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/proxy.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/reddit-alien.svg b/launcher/resources/breeze_dark/scalable/reddit-alien.svg new file mode 100644 index 00000000..00f82bb3 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/reddit-alien.svg @@ -0,0 +1,3 @@ + + + diff --git a/launcher/resources/breeze_dark/scalable/refresh.svg b/launcher/resources/breeze_dark/scalable/refresh.svg new file mode 100644 index 00000000..7b486463 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/refresh.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/rename.svg b/launcher/resources/breeze_dark/scalable/rename.svg new file mode 100644 index 00000000..6a844965 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/rename.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/resourcepacks.svg b/launcher/resources/breeze_dark/scalable/resourcepacks.svg new file mode 100644 index 00000000..0986c216 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/resourcepacks.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/screenshots.svg b/launcher/resources/breeze_dark/scalable/screenshots.svg new file mode 100644 index 00000000..a10ed713 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/screenshots.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/settings.svg b/launcher/resources/breeze_dark/scalable/settings.svg new file mode 100644 index 00000000..009d8154 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/settings.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/shaderpacks.svg b/launcher/resources/breeze_dark/scalable/shaderpacks.svg new file mode 100644 index 00000000..b2887947 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/shaderpacks.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/status-bad.svg b/launcher/resources/breeze_dark/scalable/status-bad.svg new file mode 100644 index 00000000..6fc3137e --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/status-bad.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/launcher/resources/breeze_dark/scalable/status-good.svg b/launcher/resources/breeze_dark/scalable/status-good.svg new file mode 100644 index 00000000..eb8bc03b --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/status-good.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/launcher/resources/breeze_dark/scalable/status-yellow.svg b/launcher/resources/breeze_dark/scalable/status-yellow.svg new file mode 100644 index 00000000..1dc4d0f5 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/status-yellow.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/launcher/resources/breeze_dark/scalable/tag.svg b/launcher/resources/breeze_dark/scalable/tag.svg new file mode 100644 index 00000000..b54b515f --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/tag.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/viewfolder.svg b/launcher/resources/breeze_dark/scalable/viewfolder.svg new file mode 100644 index 00000000..0189b954 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/viewfolder.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_dark/scalable/worlds.svg b/launcher/resources/breeze_dark/scalable/worlds.svg new file mode 100644 index 00000000..0cff8266 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/worlds.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/launcher/resources/breeze_light/breeze_light.qrc b/launcher/resources/breeze_light/breeze_light.qrc new file mode 100644 index 00000000..7d9d99f5 --- /dev/null +++ b/launcher/resources/breeze_light/breeze_light.qrc @@ -0,0 +1,43 @@ + + + index.theme + scalable/about.svg + scalable/accounts.svg + scalable/bug.svg + scalable/centralmods.svg + scalable/checkupdate.svg + scalable/copy.svg + scalable/coremods.svg + scalable/custom-commands.svg + scalable/discord.svg + scalable/externaltools.svg + scalable/help.svg + scalable/instance-settings.svg + scalable/jarmods.svg + scalable/java.svg + scalable/language.svg + scalable/loadermods.svg + scalable/log.svg + scalable/minecraft.svg + scalable/new.svg + scalable/news.svg + scalable/notes.svg + scalable/proxy.svg + scalable/reddit-alien.svg + scalable/refresh.svg + scalable/resourcepacks.svg + scalable/shaderpacks.svg + scalable/screenshots.svg + scalable/settings.svg + scalable/status-bad.svg + scalable/status-good.svg + scalable/status-yellow.svg + scalable/viewfolder.svg + scalable/worlds.svg + scalable/delete.svg + scalable/tag.svg + scalable/export.svg + scalable/rename.svg + scalable/launch.svg + + diff --git a/launcher/resources/breeze_light/index.theme b/launcher/resources/breeze_light/index.theme new file mode 100644 index 00000000..126d42d7 --- /dev/null +++ b/launcher/resources/breeze_light/index.theme @@ -0,0 +1,11 @@ +[Icon Theme] +Name=Breeze Light +Comment=Breeze Light Icons +Inherits=multimc +Directories=scalable + +[scalable] +Size=48 +Type=Scalable +MinSize=16 +MaxSize=256 diff --git a/launcher/resources/breeze_light/scalable/about.svg b/launcher/resources/breeze_light/scalable/about.svg new file mode 100644 index 00000000..ea1dc02c --- /dev/null +++ b/launcher/resources/breeze_light/scalable/about.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/launcher/resources/breeze_light/scalable/accounts.svg b/launcher/resources/breeze_light/scalable/accounts.svg new file mode 100644 index 00000000..8a542f36 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/accounts.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/bug.svg b/launcher/resources/breeze_light/scalable/bug.svg new file mode 100644 index 00000000..4f41ad6b --- /dev/null +++ b/launcher/resources/breeze_light/scalable/bug.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/centralmods.svg b/launcher/resources/breeze_light/scalable/centralmods.svg new file mode 100644 index 00000000..4035e51c --- /dev/null +++ b/launcher/resources/breeze_light/scalable/centralmods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_light/scalable/checkupdate.svg b/launcher/resources/breeze_light/scalable/checkupdate.svg new file mode 100644 index 00000000..06b31827 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/checkupdate.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/copy.svg b/launcher/resources/breeze_light/scalable/copy.svg new file mode 100644 index 00000000..2557953b --- /dev/null +++ b/launcher/resources/breeze_light/scalable/copy.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/launcher/resources/breeze_light/scalable/coremods.svg b/launcher/resources/breeze_light/scalable/coremods.svg new file mode 100644 index 00000000..ec4ecea8 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/coremods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_light/scalable/custom-commands.svg b/launcher/resources/breeze_light/scalable/custom-commands.svg new file mode 100644 index 00000000..b2ac78c5 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/custom-commands.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/delete.svg b/launcher/resources/breeze_light/scalable/delete.svg new file mode 100644 index 00000000..f2aea6e8 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/delete.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/discord.svg b/launcher/resources/breeze_light/scalable/discord.svg new file mode 100644 index 00000000..22ee27ba --- /dev/null +++ b/launcher/resources/breeze_light/scalable/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_light/scalable/export.svg b/launcher/resources/breeze_light/scalable/export.svg new file mode 100644 index 00000000..d6314bd7 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/export.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/launcher/resources/breeze_light/scalable/externaltools.svg b/launcher/resources/breeze_light/scalable/externaltools.svg new file mode 100644 index 00000000..c965b6c3 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/externaltools.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/help.svg b/launcher/resources/breeze_light/scalable/help.svg new file mode 100644 index 00000000..bcd14e05 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/help.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/instance-settings.svg b/launcher/resources/breeze_light/scalable/instance-settings.svg new file mode 100644 index 00000000..69854d73 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/instance-settings.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/jarmods.svg b/launcher/resources/breeze_light/scalable/jarmods.svg new file mode 100644 index 00000000..49a45d36 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/jarmods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/breeze_light/scalable/java.svg b/launcher/resources/breeze_light/scalable/java.svg new file mode 100644 index 00000000..ff86c9cc --- /dev/null +++ b/launcher/resources/breeze_light/scalable/java.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/launcher/resources/breeze_light/scalable/language.svg b/launcher/resources/breeze_light/scalable/language.svg new file mode 100644 index 00000000..3d56d33e --- /dev/null +++ b/launcher/resources/breeze_light/scalable/language.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/launch.svg b/launcher/resources/breeze_light/scalable/launch.svg new file mode 100644 index 00000000..678fd098 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/launch.svg @@ -0,0 +1,8 @@ + + + + diff --git a/launcher/resources/breeze_light/scalable/loadermods.svg b/launcher/resources/breeze_light/scalable/loadermods.svg new file mode 100644 index 00000000..4fb0f96d --- /dev/null +++ b/launcher/resources/breeze_light/scalable/loadermods.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/log.svg b/launcher/resources/breeze_light/scalable/log.svg new file mode 100644 index 00000000..cf9c9b22 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/log.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/matrix.svg b/launcher/resources/breeze_light/scalable/matrix.svg new file mode 100644 index 00000000..4745efc1 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/matrix.svg @@ -0,0 +1,9 @@ + + + Matrix (protocol) logo + + + + + + \ No newline at end of file diff --git a/launcher/resources/breeze_light/scalable/minecraft.svg b/launcher/resources/breeze_light/scalable/minecraft.svg new file mode 100644 index 00000000..1ffb4565 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/minecraft.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/launcher/resources/breeze_light/scalable/new.svg b/launcher/resources/breeze_light/scalable/new.svg new file mode 100644 index 00000000..51babd76 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/new.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/news.svg b/launcher/resources/breeze_light/scalable/news.svg new file mode 100644 index 00000000..3e3ebe95 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/news.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/notes.svg b/launcher/resources/breeze_light/scalable/notes.svg new file mode 100644 index 00000000..a8eaf279 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/notes.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/patreon.svg b/launcher/resources/breeze_light/scalable/patreon.svg new file mode 100644 index 00000000..e12f1f8d --- /dev/null +++ b/launcher/resources/breeze_light/scalable/patreon.svg @@ -0,0 +1,3 @@ + + + diff --git a/launcher/resources/breeze_light/scalable/proxy.svg b/launcher/resources/breeze_light/scalable/proxy.svg new file mode 100644 index 00000000..2e67ff6c --- /dev/null +++ b/launcher/resources/breeze_light/scalable/proxy.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/reddit-alien.svg b/launcher/resources/breeze_light/scalable/reddit-alien.svg new file mode 100644 index 00000000..93b8eedc --- /dev/null +++ b/launcher/resources/breeze_light/scalable/reddit-alien.svg @@ -0,0 +1,3 @@ + + + diff --git a/launcher/resources/breeze_light/scalable/refresh.svg b/launcher/resources/breeze_light/scalable/refresh.svg new file mode 100644 index 00000000..ecd2b394 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/refresh.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/rename.svg b/launcher/resources/breeze_light/scalable/rename.svg new file mode 100644 index 00000000..18ccc58a --- /dev/null +++ b/launcher/resources/breeze_light/scalable/rename.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/resourcepacks.svg b/launcher/resources/breeze_light/scalable/resourcepacks.svg new file mode 100644 index 00000000..913d3c1f --- /dev/null +++ b/launcher/resources/breeze_light/scalable/resourcepacks.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/launcher/resources/breeze_light/scalable/screenshots.svg b/launcher/resources/breeze_light/scalable/screenshots.svg new file mode 100644 index 00000000..d984b330 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/screenshots.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/settings.svg b/launcher/resources/breeze_light/scalable/settings.svg new file mode 100644 index 00000000..19e86e26 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/settings.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/shaderpacks.svg b/launcher/resources/breeze_light/scalable/shaderpacks.svg new file mode 100644 index 00000000..591c6af5 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/shaderpacks.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/status-bad.svg b/launcher/resources/breeze_light/scalable/status-bad.svg new file mode 100644 index 00000000..6fc3137e --- /dev/null +++ b/launcher/resources/breeze_light/scalable/status-bad.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/launcher/resources/breeze_light/scalable/status-good.svg b/launcher/resources/breeze_light/scalable/status-good.svg new file mode 100644 index 00000000..eb8bc03b --- /dev/null +++ b/launcher/resources/breeze_light/scalable/status-good.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/launcher/resources/breeze_light/scalable/status-yellow.svg b/launcher/resources/breeze_light/scalable/status-yellow.svg new file mode 100644 index 00000000..1dc4d0f5 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/status-yellow.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/launcher/resources/breeze_light/scalable/tag.svg b/launcher/resources/breeze_light/scalable/tag.svg new file mode 100644 index 00000000..4887d126 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/tag.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/viewfolder.svg b/launcher/resources/breeze_light/scalable/viewfolder.svg new file mode 100644 index 00000000..4a8498ce --- /dev/null +++ b/launcher/resources/breeze_light/scalable/viewfolder.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/scalable/worlds.svg b/launcher/resources/breeze_light/scalable/worlds.svg new file mode 100644 index 00000000..543cc55e --- /dev/null +++ b/launcher/resources/breeze_light/scalable/worlds.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 6661bf0f..cae0635f 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -303,21 +303,27 @@ void LauncherPage::applySettings() s->set("IconTheme", "pe_blue"); break; case 4: - s->set("IconTheme", "OSX"); + s->set("IconTheme", "breeze_light"); break; case 5: - s->set("IconTheme", "iOS"); + s->set("IconTheme", "breeze_dark"); break; case 6: - s->set("IconTheme", "flat"); + s->set("IconTheme", "OSX"); break; case 7: - s->set("IconTheme", "flat_white"); + s->set("IconTheme", "iOS"); break; case 8: - s->set("IconTheme", "multimc"); + s->set("IconTheme", "flat"); break; case 9: + s->set("IconTheme", "flat_white"); + break; + case 10: + s->set("IconTheme", "multimc"); + break; + case 11: s->set("IconTheme", "custom"); break; } @@ -397,7 +403,18 @@ void LauncherPage::loadSettings() m_currentUpdateChannel = s->get("UpdateChannel").toString(); //FIXME: make generic auto theme = s->get("IconTheme").toString(); - QStringList iconThemeOptions{"pe_colored", "pe_light", "pe_dark", "pe_blue", "OSX", "iOS", "flat", "flat_white", "multimc", "custom"}; + QStringList iconThemeOptions{"pe_colored", + "pe_light", + "pe_dark", + "pe_blue", + "breeze_light", + "breeze_dark", + "OSX", + "iOS", + "flat", + "flat_white", + "multimc", + "custom"}; ui->themeComboBox->setCurrentIndex(iconThemeOptions.indexOf(theme)); auto cat = s->get("BackgroundCat").toString(); diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 6de644ee..c44718a1 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -285,6 +285,16 @@ Simple (Blue Icons) + + + Breeze Light + + + + + Breeze Dark + + OSX From df0f9259c0bf79e10b27ad5b429b53559ffd15f0 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 20 Nov 2022 11:04:10 -0300 Subject: [PATCH 145/277] refactor: move RP/TP validation to their respective utils This makes it easier to validate individual resources, and allows the logic to be used in other places in the future, if we need to. Signed-off-by: flow --- .../mod/tasks/LocalResourcePackParseTask.cpp | 25 +++++++++--- .../mod/tasks/LocalResourcePackParseTask.h | 12 ++++-- .../mod/tasks/LocalTexturePackParseTask.cpp | 25 +++++++++--- .../mod/tasks/LocalTexturePackParseTask.h | 12 ++++-- launcher/ui/MainWindow.cpp | 40 +++++++++---------- 5 files changed, 76 insertions(+), 38 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index d744c535..f58a0620 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -28,14 +28,14 @@ namespace ResourcePackUtils { -bool process(ResourcePack& pack) +bool process(ResourcePack& pack, ProcessingLevel level) { switch (pack.type()) { case ResourceType::FOLDER: - ResourcePackUtils::processFolder(pack); + ResourcePackUtils::processFolder(pack, level); return true; case ResourceType::ZIPFILE: - ResourcePackUtils::processZIP(pack); + ResourcePackUtils::processZIP(pack, level); return true; default: qWarning() << "Invalid type for resource pack parse task!"; @@ -43,7 +43,7 @@ bool process(ResourcePack& pack) } } -void processFolder(ResourcePack& pack) +void processFolder(ResourcePack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::FOLDER); @@ -60,6 +60,9 @@ void processFolder(ResourcePack& pack) mcmeta_file.close(); } + if (level == ProcessingLevel::BasicInfoOnly) + return; + QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png")); if (image_file_info.isFile()) { QFile mcmeta_file(image_file_info.filePath()); @@ -74,7 +77,7 @@ void processFolder(ResourcePack& pack) } } -void processZIP(ResourcePack& pack) +void processZIP(ResourcePack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); @@ -98,6 +101,11 @@ void processZIP(ResourcePack& pack) file.close(); } + if (level == ProcessingLevel::BasicInfoOnly) { + zip.close(); + return; + } + if (zip.setCurrentFile("pack.png")) { if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Failed to open file in zip."; @@ -138,6 +146,13 @@ void processPackPNG(ResourcePack& pack, QByteArray&& raw_data) qWarning() << "Failed to parse pack.png."; } } + +bool validate(QFileInfo file) +{ + ResourcePack rp{ file }; + return ResourcePackUtils::process(rp, ProcessingLevel::BasicInfoOnly) && rp.valid(); +} + } // namespace ResourcePackUtils LocalResourcePackParseTask::LocalResourcePackParseTask(int token, ResourcePack& rp) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index d3c25464..69dbd6ad 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -26,13 +26,19 @@ #include "tasks/Task.h" namespace ResourcePackUtils { -bool process(ResourcePack& pack); -void processZIP(ResourcePack& pack); -void processFolder(ResourcePack& pack); +enum class ProcessingLevel { Full, BasicInfoOnly }; + +bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); + +void processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); +void processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); void processMCMeta(ResourcePack& pack, QByteArray&& raw_data); void processPackPNG(ResourcePack& pack, QByteArray&& raw_data); + +/** Checks whether a file is valid as a resource pack or not. */ +bool validate(QFileInfo file); } // namespace ResourcePackUtils class LocalResourcePackParseTask : public Task { diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index f23117ee..8da366c1 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -28,14 +28,14 @@ namespace TexturePackUtils { -bool process(TexturePack& pack) +bool process(TexturePack& pack, ProcessingLevel level) { switch (pack.type()) { case ResourceType::FOLDER: - TexturePackUtils::processFolder(pack); + TexturePackUtils::processFolder(pack, level); return true; case ResourceType::ZIPFILE: - TexturePackUtils::processZIP(pack); + TexturePackUtils::processZIP(pack, level); return true; default: qWarning() << "Invalid type for resource pack parse task!"; @@ -43,7 +43,7 @@ bool process(TexturePack& pack) } } -void processFolder(TexturePack& pack) +void processFolder(TexturePack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::FOLDER); @@ -60,6 +60,9 @@ void processFolder(TexturePack& pack) mcmeta_file.close(); } + if (level == ProcessingLevel::BasicInfoOnly) + return; + QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png")); if (image_file_info.isFile()) { QFile mcmeta_file(image_file_info.filePath()); @@ -74,7 +77,7 @@ void processFolder(TexturePack& pack) } } -void processZIP(TexturePack& pack) +void processZIP(TexturePack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); @@ -98,6 +101,11 @@ void processZIP(TexturePack& pack) file.close(); } + if (level == ProcessingLevel::BasicInfoOnly) { + zip.close(); + return; + } + if (zip.setCurrentFile("pack.png")) { if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Failed to open file in zip."; @@ -129,6 +137,13 @@ void processPackPNG(TexturePack& pack, QByteArray&& raw_data) qWarning() << "Failed to parse pack.png."; } } + +bool validate(QFileInfo file) +{ + TexturePack rp{ file }; + return TexturePackUtils::process(rp, ProcessingLevel::BasicInfoOnly) && rp.valid(); +} + } // namespace TexturePackUtils LocalTexturePackParseTask::LocalTexturePackParseTask(int token, TexturePack& rp) diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h index cb0e404a..9f7aab75 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h @@ -27,13 +27,19 @@ #include "tasks/Task.h" namespace TexturePackUtils { -bool process(TexturePack& pack); -void processZIP(TexturePack& pack); -void processFolder(TexturePack& pack); +enum class ProcessingLevel { Full, BasicInfoOnly }; + +bool process(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full); + +void processZIP(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full); +void processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full); void processPackTXT(TexturePack& pack, QByteArray&& raw_data); void processPackPNG(TexturePack& pack, QByteArray&& raw_data); + +/** Checks whether a file is valid as a texture pack or not. */ +bool validate(QFileInfo file); } // namespace TexturePackUtils class LocalTexturePackParseTask : public Task { diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 98fd79be..5d2a07f3 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -110,10 +110,8 @@ #include "ui/dialogs/ImportResourcePackDialog.h" #include "ui/themes/ITheme.h" -#include #include #include -#include #include #include @@ -1806,32 +1804,30 @@ void MainWindow::droppedURLs(QList urls) for (auto& url : urls) { if (url.isLocalFile()) { auto localFileName = url.toLocalFile(); - - ResourcePack rp{ QFileInfo(localFileName) }; - TexturePack tp{ QFileInfo(localFileName) }; + QFileInfo localFileInfo(localFileName); ImportResourcePackDialog dlg(this); - if (ResourcePackUtils::process(rp) && rp.valid()) { - dlg.exec(); + if (ResourcePackUtils::validate(localFileInfo)) { + dlg.exec(); - if (dlg.result() == QDialog::Accepted) { - qDebug() << "Selected instance to import resource pack into: " << dlg.selectedInstanceKey; - auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); - auto instanceButBuffed = std::dynamic_pointer_cast(instance); - instanceButBuffed->resourcePackList()->installResource(localFileName); - } - } else if (TexturePackUtils::process(tp) && tp.valid()) { - dlg.exec(); + if (dlg.result() == QDialog::Accepted) { + qDebug() << "Selected instance to import resource pack into: " << dlg.selectedInstanceKey; + auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); + auto instanceButBuffed = std::dynamic_pointer_cast(instance); + instanceButBuffed->resourcePackList()->installResource(localFileName); + } + } else if (TexturePackUtils::validate(localFileInfo)) { + dlg.exec(); - if (dlg.result() == QDialog::Accepted) { - qDebug() << "Selected instance to import texture pack into: " << dlg.selectedInstanceKey; - auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); - auto instanceButBuffed = std::dynamic_pointer_cast(instance); - instanceButBuffed->texturePackList()->installResource(localFileName); - } + if (dlg.result() == QDialog::Accepted) { + qDebug() << "Selected instance to import texture pack into: " << dlg.selectedInstanceKey; + auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); + auto instanceButBuffed = std::dynamic_pointer_cast(instance); + instanceButBuffed->texturePackList()->installResource(localFileName); + } } else { - addInstance(localFileName); + addInstance(localFileName); } } else { addInstance(url.toString()); From f55fc51e9b372e00ada739cfa679387079ffca2a Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 20 Nov 2022 11:09:24 -0300 Subject: [PATCH 146/277] fix: remove commented-out assertions They may not hold true at all times, and that's fine. Signed-off-by: flow --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 -- launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index f58a0620..6fd4b024 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -167,8 +167,6 @@ bool LocalResourcePackParseTask::abort() void LocalResourcePackParseTask::executeTask() { - // Q_ASSERT(m_resource_pack.valid()); - if (!ResourcePackUtils::process(m_resource_pack)) return; diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index 8da366c1..adb19aca 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -158,8 +158,6 @@ bool LocalTexturePackParseTask::abort() void LocalTexturePackParseTask::executeTask() { - // Q_ASSERT(m_texture_pack.valid()); - if (!TexturePackUtils::process(m_texture_pack)) return; From ffcbf97dcccb2e6cb081282a3e335ecb66d37ca5 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sun, 20 Nov 2022 09:21:43 +0100 Subject: [PATCH 147/277] fix: add Breeze copyright notice to COPYING Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- COPYING.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/COPYING.md b/COPYING.md index 3e35c579..dde3c727 100644 --- a/COPYING.md +++ b/COPYING.md @@ -398,3 +398,20 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## Breeze icons + + Copyright (C) 2014 Uri Herrera and others + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . From 884fe0d5741de2a8a78f48c5d37274d8f174ba3a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 20 Nov 2022 18:16:19 +0100 Subject: [PATCH 148/277] feat: validate maximum memory allocation in wizard Signed-off-by: Sefa Eyeoglu --- launcher/ui/widgets/JavaSettingsWidget.cpp | 42 ++++++++++++++++++---- launcher/ui/widgets/JavaSettingsWidget.h | 9 +++-- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index c7c4dbbd..15994319 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -71,6 +71,7 @@ void JavaSettingsWidget::setupUi() m_memoryGroupBox->setObjectName(QStringLiteral("memoryGroupBox")); m_gridLayout_2 = new QGridLayout(m_memoryGroupBox); m_gridLayout_2->setObjectName(QStringLiteral("gridLayout_2")); + m_gridLayout_2->setColumnStretch(0, 1); m_labelMinMem = new QLabel(m_memoryGroupBox); m_labelMinMem->setObjectName(QStringLiteral("labelMinMem")); @@ -80,7 +81,7 @@ void JavaSettingsWidget::setupUi() m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox")); m_minMemSpinBox->setSuffix(QStringLiteral(" MiB")); m_minMemSpinBox->setMinimum(128); - m_minMemSpinBox->setMaximum(m_availableMemory); + m_minMemSpinBox->setMaximum(1048576); m_minMemSpinBox->setSingleStep(128); m_labelMinMem->setBuddy(m_minMemSpinBox); m_gridLayout_2->addWidget(m_minMemSpinBox, 0, 1, 1, 1); @@ -93,11 +94,15 @@ void JavaSettingsWidget::setupUi() m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox")); m_maxMemSpinBox->setSuffix(QStringLiteral(" MiB")); m_maxMemSpinBox->setMinimum(128); - m_maxMemSpinBox->setMaximum(m_availableMemory); + m_maxMemSpinBox->setMaximum(1048576); m_maxMemSpinBox->setSingleStep(128); m_labelMaxMem->setBuddy(m_maxMemSpinBox); m_gridLayout_2->addWidget(m_maxMemSpinBox, 1, 1, 1, 1); + m_labelMaxMemIcon = new QLabel(m_memoryGroupBox); + m_labelMaxMemIcon->setObjectName(QStringLiteral("labelMaxMemIcon")); + m_gridLayout_2->addWidget(m_labelMaxMemIcon, 1, 2, 1, 1); + m_labelPermGen = new QLabel(m_memoryGroupBox); m_labelPermGen->setObjectName(QStringLiteral("labelPermGen")); m_labelPermGen->setText(QStringLiteral("PermGen:")); @@ -108,7 +113,7 @@ void JavaSettingsWidget::setupUi() m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox")); m_permGenSpinBox->setSuffix(QStringLiteral(" MiB")); m_permGenSpinBox->setMinimum(64); - m_permGenSpinBox->setMaximum(m_availableMemory); + m_permGenSpinBox->setMaximum(1048576); m_permGenSpinBox->setSingleStep(8); m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1); m_permGenSpinBox->setVisible(false); @@ -130,6 +135,7 @@ void JavaSettingsWidget::initialize() m_minMemSpinBox->setValue(observedMinMemory); m_maxMemSpinBox->setValue(observedMaxMemory); m_permGenSpinBox->setValue(observedPermGenMemory); + updateThresholds(); } void JavaSettingsWidget::refresh() @@ -210,9 +216,9 @@ int JavaSettingsWidget::permGenSize() const void JavaSettingsWidget::memoryValueChanged(int) { bool actuallyChanged = false; - int min = m_minMemSpinBox->value(); - int max = m_maxMemSpinBox->value(); - int permgen = m_permGenSpinBox->value(); + unsigned int min = m_minMemSpinBox->value(); + unsigned int max = m_maxMemSpinBox->value(); + unsigned int permgen = m_permGenSpinBox->value(); QObject *obj = sender(); if (obj == m_minMemSpinBox && min != observedMinMemory) { @@ -242,6 +248,7 @@ void JavaSettingsWidget::memoryValueChanged(int) if(actuallyChanged) { checkJavaPathOnEdit(m_javaPathTextBox->text()); + updateThresholds(); } } @@ -435,3 +442,26 @@ void JavaSettingsWidget::retranslate() m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes.")); m_javaBrowseBtn->setText(tr("Browse")); } + +void JavaSettingsWidget::updateThresholds() +{ + QString iconName; + + if (observedMaxMemory >= m_availableMemory) { + iconName = "status-bad"; + m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity.")); + } else if (observedMaxMemory > (m_availableMemory * 0.9)) { + iconName = "status-yellow"; + m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); + } else { + iconName = "status-good"; + m_labelMaxMemIcon->setToolTip(""); + } + + { + auto height = m_labelMaxMemIcon->fontInfo().pixelSize(); + QIcon icon = APPLICATION->getThemedIcon(iconName); + QPixmap pix = icon.pixmap(height, height); + m_labelMaxMemIcon->setPixmap(pix); + } +} diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index 5344e2cd..e4b7c712 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -56,6 +56,8 @@ public: int maxHeapSize() const; QString javaPath() const; + void updateThresholds(); + protected slots: void memoryValueChanged(int); @@ -85,6 +87,7 @@ private: /* data */ QSpinBox *m_maxMemSpinBox = nullptr; QLabel *m_labelMinMem = nullptr; QLabel *m_labelMaxMem = nullptr; + QLabel *m_labelMaxMemIcon = nullptr; QSpinBox *m_minMemSpinBox = nullptr; QLabel *m_labelPermGen = nullptr; QSpinBox *m_permGenSpinBox = nullptr; @@ -92,9 +95,9 @@ private: /* data */ QIcon yellowIcon; QIcon badIcon; - int observedMinMemory = 0; - int observedMaxMemory = 0; - int observedPermGenMemory = 0; + unsigned int observedMinMemory = 0; + unsigned int observedMaxMemory = 0; + unsigned int observedPermGenMemory = 0; QString queuedCheck; uint64_t m_availableMemory = 0ull; shared_qobject_ptr m_checker; From f601135cc0154236e3b26b2850f0cf633bcef2f8 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 20 Nov 2022 23:49:11 +0100 Subject: [PATCH 149/277] refactor(nix): synchronize changes from nixpkgs Signed-off-by: Sefa Eyeoglu --- flake.nix | 4 +-- nix/default.nix | 95 ++++++++++++++++++++++++------------------------- 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/flake.nix b/flake.nix index d4a25338..b1e07c91 100644 --- a/flake.nix +++ b/flake.nix @@ -23,8 +23,8 @@ pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system}); packagesFn = pkgs: rec { - prismlauncher = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; }; - prismlauncher-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; }; + prismlauncher-qt5 = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; }; + prismlauncher = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; }; }; in { diff --git a/nix/default.nix b/nix/default.nix index c7fc7576..6050fd37 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,100 +1,99 @@ -{ stdenv -, lib -, fetchFromGitHub +{ lib +, stdenv , cmake -, ninja , jdk8 , jdk -, ghc_filesystem , zlib , file , wrapQtAppsHook , xorg , libpulseaudio , qtbase -, quazip +, qtsvg +, qtwayland , libGL -, msaClientID ? "" -, extraJDKs ? [ ] +, quazip +, glfw +, openal , extra-cmake-modules +, ghc_filesystem +, msaClientID ? "" +, jdks ? [ jdk jdk8 ] # flake , self , version , libnbtplusplus , tomlplusplus -, enableLTO ? false }: -let - # Libraries required to run Minecraft - libpath = with xorg; lib.makeLibraryPath [ - libX11 - libXext - libXcursor - libXrandr - libXxf86vm - libpulseaudio - libGL - ]; - - # This variable will be passed to Minecraft by Prism Launcher - gameLibraryPath = libpath + ":/run/opengl-driver/lib"; - - javaPaths = lib.makeSearchPath "bin/java" ([ jdk jdk8 ] ++ extraJDKs); -in - stdenv.mkDerivation rec { pname = "prismlauncher"; inherit version; src = lib.cleanSource self; - nativeBuildInputs = [ cmake extra-cmake-modules ninja jdk ghc_filesystem file wrapQtAppsHook ]; - buildInputs = [ qtbase quazip zlib ]; + nativeBuildInputs = [ extra-cmake-modules cmake file jdk wrapQtAppsHook ]; + buildInputs = [ + qtbase + qtsvg + zlib + quazip + ghc_filesystem + ] ++ lib.optional (lib.versionAtLeast qtbase.version "6") qtwayland; + cmakeFlags = lib.optionals (msaClientID != "") [ "-DLauncher_MSA_CLIENT_ID=${msaClientID}" ] + ++ lib.optionals (lib.versionAtLeast qtbase.version "6") [ "-DLauncher_QT_VERSION_MAJOR=6" ]; dontWrapQtApps = true; postUnpack = '' - # Copy libnbtplusplus rm -rf source/libraries/libnbtplusplus mkdir source/libraries/libnbtplusplus ln -s ${libnbtplusplus}/* source/libraries/libnbtplusplus chmod -R +r+w source/libraries/libnbtplusplus - # Copy tomlplusplus + chown -R $USER: source/libraries/libnbtplusplus rm -rf source/libraries/tomlplusplus mkdir source/libraries/tomlplusplus ln -s ${tomlplusplus}/* source/libraries/tomlplusplus chmod -R +r+w source/libraries/tomlplusplus + chown -R $USER: 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}" ]; + postInstall = + let + libpath = with xorg; + lib.makeLibraryPath [ + libX11 + libXext + libXcursor + libXrandr + libXxf86vm + libpulseaudio + libGL + glfw + openal + stdenv.cc.cc.lib + ]; + in + '' + # xorg.xrandr needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128 + wrapQtApp $out/bin/prismlauncher \ + --set LD_LIBRARY_PATH /run/opengl-driver/lib:${libpath} \ + --prefix PRISMLAUNCHER_JAVA_PATHS : ${lib.makeSearchPath "bin/java" jdks} \ + --prefix PATH : ${lib.makeBinPath [xorg.xrandr]} + ''; - # we have to check if the system is NixOS before adding stdenv.cc.cc.lib (#923) - postInstall = '' - # xorg.xrandr needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128 - wrapQtApp $out/bin/prismlauncher \ - --run '[ -f /etc/NIXOS ] && export LD_LIBRARY_PATH="${stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH"' \ - --prefix LD_LIBRARY_PATH : ${gameLibraryPath} \ - --prefix PRISMLAUNCHER_JAVA_PATHS : ${javaPaths} \ - --prefix PATH : ${lib.makeBinPath [ xorg.xrandr ]} - ''; meta = with lib; { homepage = "https://prismlauncher.org/"; - downloadPage = "https://prismlauncher.org/download/"; - changelog = "https://github.com/PrismLauncher/PrismLauncher/releases"; description = "A free, open source launcher for Minecraft"; longDescription = '' Allows you to have multiple, separate instances of Minecraft (each with their own mods, texture packs, saves, etc) and helps you manage them and their associated options with a simple interface. ''; - platforms = platforms.unix; + platforms = platforms.linux; + changelog = "https://github.com/PrismLauncher/PrismLauncher/releases/tag/${version}"; license = licenses.gpl3Only; maintainers = with maintainers; [ minion3665 Scrumplex ]; }; From 70a11935a85325f0cf896eb301bbc342a6d3e3c8 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 21 Nov 2022 00:02:41 +0100 Subject: [PATCH 150/277] feat(actions): add Nix build Signed-off-by: Sefa Eyeoglu --- .github/workflows/build.yml | 33 ++++++++++++++++++++++++++++ .github/workflows/trigger_builds.yml | 1 + 2 files changed, 34 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c0e5b50a..d41dfdc2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,9 @@ on: SPARKLE_ED25519_KEY: description: Private key for signing Sparkle updates required: false + CACHIX_AUTH_TOKEN: + description: Private token for authenticating against Cachix cache + required: false jobs: build: @@ -526,3 +529,33 @@ jobs: bundle: "Prism Launcher.flatpak" manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml cache-key: flatpak-${{ github.sha }}-x86_64 + + nix: + runs-on: ubuntu-latest + strategy: + matrix: + package: + - prismlauncher + - prismlauncher-qt5 + steps: + - name: Clone repository + if: inputs.build_type == 'Debug' + uses: actions/checkout@v3 + with: + submodules: 'true' + - name: Install nix + if: inputs.build_type == 'Debug' + uses: cachix/install-nix-action@v18 + with: + install_url: https://nixos.org/nix/install + extra_nix_config: | + auto-optimise-store = true + experimental-features = nix-command flakes + - uses: cachix/cachix-action@v12 + if: inputs.build_type == 'Debug' + with: + name: prismlauncher + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - name: Build + if: inputs.build_type == 'Debug' + run: nix build .#${{ matrix.package }} --print-build-logs diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 44751fbc..cf299744 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -33,3 +33,4 @@ jobs: is_qt_cached: true secrets: SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }} + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} From 7096f02b88e982df6c770113146a07874a1e9d0f Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Mon, 21 Nov 2022 18:15:03 +0800 Subject: [PATCH 151/277] fix: text wrapping Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/ui/dialogs/ImportResourcePackDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.cpp b/launcher/ui/dialogs/ImportResourcePackDialog.cpp index 2b746605..7a2152b7 100644 --- a/launcher/ui/dialogs/ImportResourcePackDialog.cpp +++ b/launcher/ui/dialogs/ImportResourcePackDialog.cpp @@ -27,7 +27,7 @@ ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(pa contentsWidget->setWordWrap(false); contentsWidget->setWrapping(true); contentsWidget->setUniformItemSizes(true); - contentsWidget->setTextElideMode(Qt::ElideRight); + contentsWidget->setWordWrap(true); contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); From c1b3a3adb4be8b31c23ded08f51a477bfc36d9a3 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 21 Nov 2022 11:43:46 +0100 Subject: [PATCH 152/277] fix(actions): don't ignore nix files Signed-off-by: Sefa Eyeoglu --- .github/workflows/trigger_builds.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index cf299744..a08193a0 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -8,7 +8,6 @@ on: - '**.md' - '**/LICENSE' - 'flake.lock' - - '**.nix' - 'packages/**' - '.github/ISSUE_TEMPLATE/**' - '.markdownlint**' @@ -17,7 +16,6 @@ on: - '**.md' - '**/LICENSE' - 'flake.lock' - - '**.nix' - 'packages/**' - '.github/ISSUE_TEMPLATE/**' - '.markdownlint**' From 4208c2c72a160896f63f44148c74cf24fc30d4b9 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Mon, 21 Nov 2022 15:47:15 +0100 Subject: [PATCH 153/277] fix: actually emit fileCopied Signed-off-by: Sefa Eyeoglu --- launcher/FileSystem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 0c6527b1..987f4e74 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -188,6 +188,8 @@ bool copy::operator()(const QString& offset, bool dryRun) qDebug() << "Source file:" << src_path; qDebug() << "Destination file:" << dst_path; } + m_copied++; + emit fileCopied(relative_dst_path); }; // We can't use copy_opts::recursive because we need to take into account the From 66f9fed2f2fccfb00ee3086637a667eca60213aa Mon Sep 17 00:00:00 2001 From: Trisave <42098407+Protrikk@users.noreply.github.com> Date: Tue, 22 Nov 2022 18:30:21 +0100 Subject: [PATCH 154/277] Remove blue-ish tint from flat white icons Signed-off-by: Trisave <42098407+Protrikk@users.noreply.github.com> --- launcher/resources/flat_white/scalable/about.svg | 4 ++-- launcher/resources/flat_white/scalable/accounts.svg | 4 ++-- launcher/resources/flat_white/scalable/bug.svg | 4 ++-- launcher/resources/flat_white/scalable/cat.svg | 4 ++-- launcher/resources/flat_white/scalable/centralmods.svg | 4 ++-- launcher/resources/flat_white/scalable/checkupdate.svg | 4 ++-- launcher/resources/flat_white/scalable/copy.svg | 4 ++-- launcher/resources/flat_white/scalable/coremods.svg | 4 ++-- .../resources/flat_white/scalable/custom-commands.svg | 2 +- launcher/resources/flat_white/scalable/delete.svg | 6 +++--- launcher/resources/flat_white/scalable/discord.svg | 4 ++-- launcher/resources/flat_white/scalable/export.svg | 4 ++-- launcher/resources/flat_white/scalable/externaltools.svg | 4 ++-- launcher/resources/flat_white/scalable/help.svg | 2 +- .../resources/flat_white/scalable/instance-settings.svg | 4 ++-- launcher/resources/flat_white/scalable/jarmods.svg | 4 ++-- launcher/resources/flat_white/scalable/java.svg | 4 ++-- launcher/resources/flat_white/scalable/language.svg | 2 +- launcher/resources/flat_white/scalable/launch.svg | 2 +- launcher/resources/flat_white/scalable/launcher.svg | 2 +- launcher/resources/flat_white/scalable/loadermods.svg | 4 ++-- launcher/resources/flat_white/scalable/log.svg | 4 ++-- launcher/resources/flat_white/scalable/minecraft.svg | 4 ++-- launcher/resources/flat_white/scalable/multimc.svg | 4 ++-- launcher/resources/flat_white/scalable/new.svg | 4 ++-- launcher/resources/flat_white/scalable/news.svg | 4 ++-- launcher/resources/flat_white/scalable/notes.svg | 4 ++-- launcher/resources/flat_white/scalable/packages.svg | 4 ++-- launcher/resources/flat_white/scalable/patreon.svg | 4 ++-- launcher/resources/flat_white/scalable/proxy.svg | 4 ++-- launcher/resources/flat_white/scalable/quickmods.svg | 4 ++-- launcher/resources/flat_white/scalable/reddit-alien.svg | 4 ++-- launcher/resources/flat_white/scalable/refresh.svg | 4 ++-- launcher/resources/flat_white/scalable/rename.svg | 4 ++-- launcher/resources/flat_white/scalable/resourcepacks.svg | 4 ++-- .../flat_white/scalable/screenshot-placeholder.svg | 4 ++-- launcher/resources/flat_white/scalable/screenshots.svg | 4 ++-- launcher/resources/flat_white/scalable/settings.svg | 4 ++-- launcher/resources/flat_white/scalable/shaderpacks.svg | 8 ++++---- launcher/resources/flat_white/scalable/star.svg | 4 ++-- launcher/resources/flat_white/scalable/status-bad.svg | 4 ++-- launcher/resources/flat_white/scalable/status-good.svg | 4 ++-- launcher/resources/flat_white/scalable/status-running.svg | 4 ++-- launcher/resources/flat_white/scalable/status-yellow.svg | 4 ++-- launcher/resources/flat_white/scalable/tag.svg | 6 +++--- launcher/resources/flat_white/scalable/viewfolder.svg | 4 ++-- launcher/resources/flat_white/scalable/worlds.svg | 4 ++-- 47 files changed, 93 insertions(+), 93 deletions(-) diff --git a/launcher/resources/flat_white/scalable/about.svg b/launcher/resources/flat_white/scalable/about.svg index e42ca948..e2071c84 100644 --- a/launcher/resources/flat_white/scalable/about.svg +++ b/launcher/resources/flat_white/scalable/about.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/accounts.svg b/launcher/resources/flat_white/scalable/accounts.svg index e714bde1..0b413e2a 100644 --- a/launcher/resources/flat_white/scalable/accounts.svg +++ b/launcher/resources/flat_white/scalable/accounts.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/bug.svg b/launcher/resources/flat_white/scalable/bug.svg index 3122702e..1e270acd 100644 --- a/launcher/resources/flat_white/scalable/bug.svg +++ b/launcher/resources/flat_white/scalable/bug.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/cat.svg b/launcher/resources/flat_white/scalable/cat.svg index 18da097a..93470c4f 100644 --- a/launcher/resources/flat_white/scalable/cat.svg +++ b/launcher/resources/flat_white/scalable/cat.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/centralmods.svg b/launcher/resources/flat_white/scalable/centralmods.svg index d8d10f2f..277fe111 100644 --- a/launcher/resources/flat_white/scalable/centralmods.svg +++ b/launcher/resources/flat_white/scalable/centralmods.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/checkupdate.svg b/launcher/resources/flat_white/scalable/checkupdate.svg index 0fa66fc2..78db2b0c 100644 --- a/launcher/resources/flat_white/scalable/checkupdate.svg +++ b/launcher/resources/flat_white/scalable/checkupdate.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/copy.svg b/launcher/resources/flat_white/scalable/copy.svg index 1aaed97b..abcb2b69 100644 --- a/launcher/resources/flat_white/scalable/copy.svg +++ b/launcher/resources/flat_white/scalable/copy.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/coremods.svg b/launcher/resources/flat_white/scalable/coremods.svg index 32c34383..f3132a5f 100644 --- a/launcher/resources/flat_white/scalable/coremods.svg +++ b/launcher/resources/flat_white/scalable/coremods.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/custom-commands.svg b/launcher/resources/flat_white/scalable/custom-commands.svg index 3d67d8f1..fe1cf998 100644 --- a/launcher/resources/flat_white/scalable/custom-commands.svg +++ b/launcher/resources/flat_white/scalable/custom-commands.svg @@ -1,6 +1,6 @@ - + - - \ No newline at end of file + + diff --git a/launcher/resources/flat_white/scalable/discord.svg b/launcher/resources/flat_white/scalable/discord.svg index ee07ed25..6a07d228 100644 --- a/launcher/resources/flat_white/scalable/discord.svg +++ b/launcher/resources/flat_white/scalable/discord.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/export.svg b/launcher/resources/flat_white/scalable/export.svg index a28bb254..09595211 100644 --- a/launcher/resources/flat_white/scalable/export.svg +++ b/launcher/resources/flat_white/scalable/export.svg @@ -1,5 +1,5 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/externaltools.svg b/launcher/resources/flat_white/scalable/externaltools.svg index e7c0930c..d641f4f2 100644 --- a/launcher/resources/flat_white/scalable/externaltools.svg +++ b/launcher/resources/flat_white/scalable/externaltools.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/help.svg b/launcher/resources/flat_white/scalable/help.svg index 82b413fe..31e8c092 100644 --- a/launcher/resources/flat_white/scalable/help.svg +++ b/launcher/resources/flat_white/scalable/help.svg @@ -10,7 +10,7 @@ width="24" viewBox="0 0 24 24" height="24" - fill="#D8DEE9"> + fill="#eeeeee"> diff --git a/launcher/resources/flat_white/scalable/instance-settings.svg b/launcher/resources/flat_white/scalable/instance-settings.svg index 7dac7b14..95a0a802 100644 --- a/launcher/resources/flat_white/scalable/instance-settings.svg +++ b/launcher/resources/flat_white/scalable/instance-settings.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/jarmods.svg b/launcher/resources/flat_white/scalable/jarmods.svg index f0f298f3..603a8ae9 100644 --- a/launcher/resources/flat_white/scalable/jarmods.svg +++ b/launcher/resources/flat_white/scalable/jarmods.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/java.svg b/launcher/resources/flat_white/scalable/java.svg index 56bb481f..db81128e 100644 --- a/launcher/resources/flat_white/scalable/java.svg +++ b/launcher/resources/flat_white/scalable/java.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/language.svg b/launcher/resources/flat_white/scalable/language.svg index 18c22efb..4aef2946 100644 --- a/launcher/resources/flat_white/scalable/language.svg +++ b/launcher/resources/flat_white/scalable/language.svg @@ -7,7 +7,7 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - fill="#D8DEE9" + fill="#eeeeee" height="24" viewBox="0 0 24 24" width="24" diff --git a/launcher/resources/flat_white/scalable/launch.svg b/launcher/resources/flat_white/scalable/launch.svg index 9e759431..ddd6d5f2 100644 --- a/launcher/resources/flat_white/scalable/launch.svg +++ b/launcher/resources/flat_white/scalable/launch.svg @@ -1,6 +1,6 @@ - + diff --git a/launcher/resources/flat_white/scalable/loadermods.svg b/launcher/resources/flat_white/scalable/loadermods.svg index 100f7a93..95c72084 100644 --- a/launcher/resources/flat_white/scalable/loadermods.svg +++ b/launcher/resources/flat_white/scalable/loadermods.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/log.svg b/launcher/resources/flat_white/scalable/log.svg index 69b7c1dc..a40139d3 100644 --- a/launcher/resources/flat_white/scalable/log.svg +++ b/launcher/resources/flat_white/scalable/log.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/minecraft.svg b/launcher/resources/flat_white/scalable/minecraft.svg index a0348e79..94aaebd1 100644 --- a/launcher/resources/flat_white/scalable/minecraft.svg +++ b/launcher/resources/flat_white/scalable/minecraft.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/multimc.svg b/launcher/resources/flat_white/scalable/multimc.svg index 3dce2699..9afe68d9 100644 --- a/launcher/resources/flat_white/scalable/multimc.svg +++ b/launcher/resources/flat_white/scalable/multimc.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/new.svg b/launcher/resources/flat_white/scalable/new.svg index 46dc3361..22c6a6fe 100644 --- a/launcher/resources/flat_white/scalable/new.svg +++ b/launcher/resources/flat_white/scalable/new.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/news.svg b/launcher/resources/flat_white/scalable/news.svg index 414e5454..76623f34 100644 --- a/launcher/resources/flat_white/scalable/news.svg +++ b/launcher/resources/flat_white/scalable/news.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/notes.svg b/launcher/resources/flat_white/scalable/notes.svg index 4ce5f6f1..18a1265d 100644 --- a/launcher/resources/flat_white/scalable/notes.svg +++ b/launcher/resources/flat_white/scalable/notes.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/packages.svg b/launcher/resources/flat_white/scalable/packages.svg index 909ad0b2..d2c87955 100644 --- a/launcher/resources/flat_white/scalable/packages.svg +++ b/launcher/resources/flat_white/scalable/packages.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/patreon.svg b/launcher/resources/flat_white/scalable/patreon.svg index b745765b..d5385eac 100644 --- a/launcher/resources/flat_white/scalable/patreon.svg +++ b/launcher/resources/flat_white/scalable/patreon.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/proxy.svg b/launcher/resources/flat_white/scalable/proxy.svg index a86703f4..30e27e8a 100644 --- a/launcher/resources/flat_white/scalable/proxy.svg +++ b/launcher/resources/flat_white/scalable/proxy.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/quickmods.svg b/launcher/resources/flat_white/scalable/quickmods.svg index 9e0045b2..599bd2bf 100644 --- a/launcher/resources/flat_white/scalable/quickmods.svg +++ b/launcher/resources/flat_white/scalable/quickmods.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/reddit-alien.svg b/launcher/resources/flat_white/scalable/reddit-alien.svg index be22148c..291b12e0 100644 --- a/launcher/resources/flat_white/scalable/reddit-alien.svg +++ b/launcher/resources/flat_white/scalable/reddit-alien.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/refresh.svg b/launcher/resources/flat_white/scalable/refresh.svg index 08c63bdf..e8c6c44b 100644 --- a/launcher/resources/flat_white/scalable/refresh.svg +++ b/launcher/resources/flat_white/scalable/refresh.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/rename.svg b/launcher/resources/flat_white/scalable/rename.svg index f2067c16..e7d6634a 100644 --- a/launcher/resources/flat_white/scalable/rename.svg +++ b/launcher/resources/flat_white/scalable/rename.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/resourcepacks.svg b/launcher/resources/flat_white/scalable/resourcepacks.svg index 9dd73c3a..272af76b 100644 --- a/launcher/resources/flat_white/scalable/resourcepacks.svg +++ b/launcher/resources/flat_white/scalable/resourcepacks.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/screenshot-placeholder.svg b/launcher/resources/flat_white/scalable/screenshot-placeholder.svg index 41eb6fcf..162b7804 100644 --- a/launcher/resources/flat_white/scalable/screenshot-placeholder.svg +++ b/launcher/resources/flat_white/scalable/screenshot-placeholder.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/screenshots.svg b/launcher/resources/flat_white/scalable/screenshots.svg index 68cf8969..ae1c876d 100644 --- a/launcher/resources/flat_white/scalable/screenshots.svg +++ b/launcher/resources/flat_white/scalable/screenshots.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/settings.svg b/launcher/resources/flat_white/scalable/settings.svg index 7dac7b14..95a0a802 100644 --- a/launcher/resources/flat_white/scalable/settings.svg +++ b/launcher/resources/flat_white/scalable/settings.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/shaderpacks.svg b/launcher/resources/flat_white/scalable/shaderpacks.svg index ccae221c..bfd8b833 100644 --- a/launcher/resources/flat_white/scalable/shaderpacks.svg +++ b/launcher/resources/flat_white/scalable/shaderpacks.svg @@ -1,6 +1,6 @@ diff --git a/launcher/resources/flat_white/scalable/star.svg b/launcher/resources/flat_white/scalable/star.svg index 116f952e..2a573ca3 100644 --- a/launcher/resources/flat_white/scalable/star.svg +++ b/launcher/resources/flat_white/scalable/star.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/status-bad.svg b/launcher/resources/flat_white/scalable/status-bad.svg index 5a121c09..b6b42a96 100644 --- a/launcher/resources/flat_white/scalable/status-bad.svg +++ b/launcher/resources/flat_white/scalable/status-bad.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/status-good.svg b/launcher/resources/flat_white/scalable/status-good.svg index ccb732ab..aee4c523 100644 --- a/launcher/resources/flat_white/scalable/status-good.svg +++ b/launcher/resources/flat_white/scalable/status-good.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/status-running.svg b/launcher/resources/flat_white/scalable/status-running.svg index aa2d5fbf..d4d55194 100644 --- a/launcher/resources/flat_white/scalable/status-running.svg +++ b/launcher/resources/flat_white/scalable/status-running.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/status-yellow.svg b/launcher/resources/flat_white/scalable/status-yellow.svg index 772699d3..00737f51 100644 --- a/launcher/resources/flat_white/scalable/status-yellow.svg +++ b/launcher/resources/flat_white/scalable/status-yellow.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/tag.svg b/launcher/resources/flat_white/scalable/tag.svg index f91fb0b4..0d7661e0 100644 --- a/launcher/resources/flat_white/scalable/tag.svg +++ b/launcher/resources/flat_white/scalable/tag.svg @@ -1,4 +1,4 @@ - - - \ No newline at end of file + + + diff --git a/launcher/resources/flat_white/scalable/viewfolder.svg b/launcher/resources/flat_white/scalable/viewfolder.svg index 145f8624..b13c8eb3 100644 --- a/launcher/resources/flat_white/scalable/viewfolder.svg +++ b/launcher/resources/flat_white/scalable/viewfolder.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + diff --git a/launcher/resources/flat_white/scalable/worlds.svg b/launcher/resources/flat_white/scalable/worlds.svg index cea30cf8..d7aaef1d 100644 --- a/launcher/resources/flat_white/scalable/worlds.svg +++ b/launcher/resources/flat_white/scalable/worlds.svg @@ -1,3 +1,3 @@ - + - \ No newline at end of file + From 20c281d6f8d5f25573a8c4c930a961ea9ab45380 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 22 Nov 2022 14:30:54 -0300 Subject: [PATCH 155/277] fix: reset wide bar settings when the list of actions changes This prevents changes to the actions to cause non-intuitive issues in the Wide bar, hiding items that previously weren't hidden. Signed-off-by: flow --- launcher/ui/widgets/WideBar.cpp | 35 +++++++++++++++++++++++++++++++-- launcher/ui/widgets/WideBar.h | 4 ++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index 2ad2caec..81c445cb 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -1,6 +1,7 @@ #include "WideBar.h" #include +#include #include class ActionButton : public QToolButton { @@ -211,23 +212,53 @@ void WideBar::contextMenuEvent(QContextMenuEvent* event) state.append(entry.bar_action->isVisible() ? '1' : '0'); } + state.append(','); + state.append(getHash()); + return state; } void WideBar::setVisibilityState(QByteArray&& state) { + auto split = state.split(','); + + auto bits = split.first(); + auto hash = split.last(); + + // If the actions changed, we better not try to load the old one to avoid unwanted hiding + if (!checkHash(hash)) + return; + qsizetype i = 0; for (auto& entry : m_entries) { if (entry.type != BarEntry::Type::Action) continue; - if (i == state.size()) + if (i == bits.size()) break; - entry.bar_action->setVisible(state.at(i++) == '1'); + entry.bar_action->setVisible(bits.at(i++) == '1'); // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible. static_cast(widgetForAction(entry.bar_action))->actionChanged(); } } +QByteArray WideBar::getHash() const +{ + QCryptographicHash hash(QCryptographicHash::Sha1); + for (auto const& entry : m_entries) { + if (entry.type != BarEntry::Type::Action) + continue; + hash.addData(entry.menu_action->text().toLatin1()); + } + + return hash.result().toBase64(); +} + +bool WideBar::checkHash(QByteArray const& old_hash) const +{ + return old_hash == getHash(); +} + + #include "WideBar.moc" diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h index 0d60f8a4..ed4cb3c7 100644 --- a/launcher/ui/widgets/WideBar.h +++ b/launcher/ui/widgets/WideBar.h @@ -41,6 +41,10 @@ class WideBar : public QToolBar { auto getMatching(QAction* act) -> QList::iterator; + /** Used to distinguish between versions of the WideBar with different actions */ + [[nodiscard]] QByteArray getHash() const; + [[nodiscard]] bool checkHash(QByteArray const&) const; + private: QList m_entries; From 980f03dd206d9e15ceb111839ebc615a493c790f Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Tue, 22 Nov 2022 19:17:16 +0000 Subject: [PATCH 156/277] Fix: Launcher_FORCE_BUNDLED_LIBS Failing to find bundled zlib Signed-off-by: TheLastRar --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fc0d326..f04b733b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,11 +368,11 @@ if(NOT ZLIB_FOUND) set(SKIP_INSTALL_ALL ON) add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL) - set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib") + set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" CACHE STRING "") set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}") add_library(ZLIB::ZLIB ALIAS zlibstatic) - set(ZLIB_LIBRARY ZLIB::ZLIB) - set(ZLIB_FOUND true) + set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name") + find_package(ZLIB REQUIRED) else() message(STATUS "Using system zlib") From 96e8217b0034a7d73c7a37881955c51d3b248dca Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 8 Nov 2022 11:20:13 +0000 Subject: [PATCH 157/277] Button to add agent Signed-off-by: TheKodeToad --- launcher/minecraft/Agent.h | 2 +- launcher/minecraft/PackProfile.cpp | 72 ++++++++++++++++++++-- launcher/minecraft/PackProfile.h | 7 ++- launcher/ui/pages/instance/VersionPage.cpp | 16 ++++- launcher/ui/pages/instance/VersionPage.h | 4 +- launcher/ui/pages/instance/VersionPage.ui | 9 +++ 6 files changed, 101 insertions(+), 9 deletions(-) diff --git a/launcher/minecraft/Agent.h b/launcher/minecraft/Agent.h index 01109daf..374e6e94 100644 --- a/launcher/minecraft/Agent.h +++ b/launcher/minecraft/Agent.h @@ -10,7 +10,7 @@ typedef std::shared_ptr AgentPtr; class Agent { public: - Agent(LibraryPtr library, QString &argument) + Agent(LibraryPtr library, const QString &argument) { m_library = library; m_argument = argument; diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 6ce525eb..bbdf51d8 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -47,7 +48,6 @@ #include "Exception.h" #include "minecraft/OneSixVersionFormat.h" #include "FileSystem.h" -#include "meta/Index.h" #include "minecraft/MinecraftInstance.h" #include "Json.h" @@ -55,7 +55,6 @@ #include "PackProfile_p.h" #include "ComponentUpdateTask.h" -#include "Application.h" #include "modplatform/ModAPI.h" static const QMap modloaderMapping{ @@ -738,6 +737,11 @@ void PackProfile::installCustomJar(QString selectedFile) installCustomJar_internal(selectedFile); } +void PackProfile::installAgents(QStringList selectedFiles) +{ + installAgents_internal(selectedFiles); +} + bool PackProfile::installEmpty(const QString& uid, const QString& name) { QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); @@ -832,8 +836,7 @@ bool PackProfile::installJarMods_internal(QStringList filepaths) for(auto filepath:filepaths) { QFileInfo sourceInfo(filepath); - auto uuid = QUuid::createUuid(); - QString id = uuid.toString().remove('{').remove('}'); + QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); QString target_filename = id + ".jar"; QString target_id = "org.multimc.jarmod." + id; QString target_name = sourceInfo.completeBaseName() + " (jar mod)"; @@ -939,6 +942,65 @@ bool PackProfile::installCustomJar_internal(QString filepath) return true; } +bool PackProfile::installAgents_internal(QStringList filepaths) +{ + // FIXME code duplication + const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); + if (!FS::ensureFolderPathExists(patchDir)) + return false; + + const QString libDir = d->m_instance->getLocalLibraryPath(); + if (!FS::ensureFolderPathExists(libDir)) + return false; + + for (const QString& source : filepaths) { + const QFileInfo sourceInfo(source); + const QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); + const QString targetBaseName = id + ".jar"; + const QString targetId = "org.prismlauncher.agent." + id; + const QString targetName = sourceInfo.completeBaseName() + " (agent)"; + const QString target = FS::PathCombine(d->m_instance->getLocalLibraryPath(), targetBaseName); + + const QFileInfo targetInfo(target); + if (targetInfo.exists()) + return false; + + if (!QFile::copy(source, target)) + return false; + + auto versionFile = std::make_shared(); + + auto agent = std::make_shared(); + + agent->setRawName("org.prismlauncher.agents:" + id + ":1"); + agent->setFilename(targetBaseName); + agent->setDisplayName(sourceInfo.completeBaseName()); + agent->setHint("local"); + + versionFile->agents.append(std::make_shared(agent, QString())); + + versionFile->name = targetName; + versionFile->uid = targetId; + + QFile patchFile(FS::PathCombine(patchDir, targetId + ".json")); + + if (!patchFile.open(QFile::WriteOnly)) { + qCritical() << "Error opening" << patchFile.fileName() << "for reading:" << patchFile.errorString(); + return false; + } + + patchFile.write(OneSixVersionFormat::versionFileToJson(versionFile).toJson()); + patchFile.close(); + + appendComponent(new Component(this, versionFile->uid, versionFile)); + } + + scheduleSave(); + invalidateLaunchProfile(); + + return true; +} + std::shared_ptr PackProfile::getProfile() const { if(!d->m_profile) diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h index 807511a2..2330cca1 100644 --- a/launcher/minecraft/PackProfile.h +++ b/launcher/minecraft/PackProfile.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -85,6 +86,9 @@ public: /// install a jar/zip as a replacement for the main jar void installCustomJar(QString selectedFile); + /// install Java agent files + void installAgents(QStringList selectedFiles); + enum MoveDirection { MoveUp, MoveDown }; /// move component file # up or down the list void move(const int index, const MoveDirection direction); @@ -167,6 +171,7 @@ private: bool load(); bool installJarMods_internal(QStringList filepaths); bool installCustomJar_internal(QString filepath); + bool installAgents_internal(QStringList filepaths); bool removeComponent_internal(ComponentPtr patch); private: /* data */ diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index a021c633..7f98cba2 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2022 TheKodeToad * * 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 @@ -270,6 +271,7 @@ void VersionPage::updateButtons(int row) ui->actionInstall_mods->setEnabled(controlsEnabled); ui->actionReplace_Minecraft_jar->setEnabled(controlsEnabled); ui->actionAdd_to_Minecraft_jar->setEnabled(controlsEnabled); + ui->actionAdd_Agents->setEnabled(controlsEnabled); } bool VersionPage::reloadPackProfile() @@ -342,6 +344,18 @@ void VersionPage::on_actionReplace_Minecraft_jar_triggered() updateButtons(); } + +void VersionPage::on_actionAdd_Agents_triggered() +{ + QStringList list = GuiUtil::BrowseForFiles("agent", tr("Select agents"), tr("Java agents (*.jar)"), + APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget()); + + if (!list.isEmpty()) + m_profile->installAgents(list); + + updateButtons(); +} + void VersionPage::on_actionMove_up_triggered() { try diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 979311fc..23d2a1b3 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield + * Copyright (C) 2022 TheKodeToad * * 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 @@ -82,6 +83,7 @@ private slots: void on_actionMove_down_triggered(); void on_actionAdd_to_Minecraft_jar_triggered(); void on_actionReplace_Minecraft_jar_triggered(); + void on_actionAdd_Agents_triggered(); void on_actionRevert_triggered(); void on_actionEdit_triggered(); void on_actionInstall_mods_triggered(); diff --git a/launcher/ui/pages/instance/VersionPage.ui b/launcher/ui/pages/instance/VersionPage.ui index 14b7cd9f..74b9568a 100644 --- a/launcher/ui/pages/instance/VersionPage.ui +++ b/launcher/ui/pages/instance/VersionPage.ui @@ -109,6 +109,7 @@ + @@ -226,6 +227,14 @@ Replace Minecraft.jar + + + Add Agents + + + Add Java agents. + + Add Empty From 6a51eda36171d58cff9d66f4c7a2172f32fa2314 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 23 Nov 2022 19:53:55 +0100 Subject: [PATCH 158/277] fix: force bundled libs on win msvc Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c0e5b50a..4dfb45d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -230,7 +230,7 @@ jobs: - name: Configure CMake (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' 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 }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} + 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 }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { From 4a1d08261408b63308dc1164c687e53c4f1fd08e Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 25 Nov 2022 09:33:05 -0300 Subject: [PATCH 159/277] reafctor(WideBar): connect to signal instead of overriding menu method This makes stuff more standard and closer to what we do in other places in the codebase. Signed-off-by: flow --- launcher/ui/widgets/WideBar.cpp | 13 +++++++++---- launcher/ui/widgets/WideBar.h | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index 81c445cb..428be563 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -38,7 +38,8 @@ WideBar::WideBar(const QString& title, QWidget* parent) : QToolBar(title, parent setFloatable(false); setMovable(false); - m_bar_menu = std::make_unique(this); + setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); + connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu); } WideBar::WideBar(QWidget* parent) : QToolBar(parent) @@ -46,7 +47,8 @@ WideBar::WideBar(QWidget* parent) : QToolBar(parent) setFloatable(false); setMovable(false); - m_bar_menu = std::make_unique(this); + setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); + connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu); } void WideBar::addAction(QAction* action) @@ -167,8 +169,11 @@ static void copyAction(QAction* from, QAction* to) to->setToolTip(from->toolTip()); } -void WideBar::contextMenuEvent(QContextMenuEvent* event) +void WideBar::showVisibilityMenu(QPoint const& position) { + if (!m_bar_menu) + m_bar_menu = std::make_unique(this); + if (m_menu_state == MenuState::Dirty) { for (auto* old_action : m_bar_menu->actions()) old_action->deleteLater(); @@ -198,7 +203,7 @@ void WideBar::contextMenuEvent(QContextMenuEvent* event) m_menu_state = MenuState::Fresh; } - m_bar_menu->popup(event->globalPos()); + m_bar_menu->popup(mapToGlobal(position)); } [[nodiscard]] QByteArray WideBar::getVisibilityState() const diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h index ed4cb3c7..a0a7896c 100644 --- a/launcher/ui/widgets/WideBar.h +++ b/launcher/ui/widgets/WideBar.h @@ -24,7 +24,7 @@ class WideBar : public QToolBar { void insertActionAfter(QAction* after, QAction* action); QMenu* createContextMenu(QWidget* parent = nullptr, const QString& title = QString()); - void contextMenuEvent(QContextMenuEvent*) override; + void showVisibilityMenu(const QPoint&); // Ideally we would use a QBitArray for this, but it doesn't support string conversion, // so using it in settings is very messy. From cbf4159c7e21c0dc70e36db5bbcc4162c72f31b5 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 25 Nov 2022 13:54:13 +0100 Subject: [PATCH 160/277] fix: add missing shortcut icons Signed-off-by: Sefa Eyeoglu --- .../resources/breeze_dark/breeze_dark.qrc | 1 + .../breeze_dark/scalable/shortcut.svg | 15 ++ .../resources/breeze_light/breeze_light.qrc | 1 + .../breeze_light/scalable/shortcut.svg | 15 ++ launcher/resources/flat/flat.qrc | 1 + launcher/resources/flat/scalable/shortcut.svg | 3 + launcher/resources/flat_white/flat_white.qrc | 1 + .../flat_white/scalable/shortcut.svg | 3 + launcher/resources/multimc/multimc.qrc | 3 +- .../resources/multimc/scalable/shortcut.svg | 157 ++++++++++++++++++ 10 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 launcher/resources/breeze_dark/scalable/shortcut.svg create mode 100644 launcher/resources/breeze_light/scalable/shortcut.svg create mode 100644 launcher/resources/flat/scalable/shortcut.svg create mode 100644 launcher/resources/flat_white/scalable/shortcut.svg create mode 100644 launcher/resources/multimc/scalable/shortcut.svg diff --git a/launcher/resources/breeze_dark/breeze_dark.qrc b/launcher/resources/breeze_dark/breeze_dark.qrc index 4d7a69b2..97434abc 100644 --- a/launcher/resources/breeze_dark/breeze_dark.qrc +++ b/launcher/resources/breeze_dark/breeze_dark.qrc @@ -27,6 +27,7 @@ scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg + scalable/shortcut.svg scalable/screenshots.svg scalable/settings.svg scalable/status-bad.svg diff --git a/launcher/resources/breeze_dark/scalable/shortcut.svg b/launcher/resources/breeze_dark/scalable/shortcut.svg new file mode 100644 index 00000000..2f24b8c0 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/shortcut.svg @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/launcher/resources/breeze_light/breeze_light.qrc b/launcher/resources/breeze_light/breeze_light.qrc index 7d9d99f5..6d868b18 100644 --- a/launcher/resources/breeze_light/breeze_light.qrc +++ b/launcher/resources/breeze_light/breeze_light.qrc @@ -27,6 +27,7 @@ scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg + scalable/shortcut.svg scalable/screenshots.svg scalable/settings.svg scalable/status-bad.svg diff --git a/launcher/resources/breeze_light/scalable/shortcut.svg b/launcher/resources/breeze_light/scalable/shortcut.svg new file mode 100644 index 00000000..96def1f8 --- /dev/null +++ b/launcher/resources/breeze_light/scalable/shortcut.svg @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/launcher/resources/flat/flat.qrc b/launcher/resources/flat/flat.qrc index 508e0a9f..a846bd2d 100644 --- a/launcher/resources/flat/flat.qrc +++ b/launcher/resources/flat/flat.qrc @@ -35,6 +35,7 @@ scalable/screenshot-placeholder.svg scalable/screenshots.svg scalable/settings.svg + scalable/shortcut.svg scalable/star.svg scalable/status-bad.svg scalable/status-good.svg diff --git a/launcher/resources/flat/scalable/shortcut.svg b/launcher/resources/flat/scalable/shortcut.svg new file mode 100644 index 00000000..83878d19 --- /dev/null +++ b/launcher/resources/flat/scalable/shortcut.svg @@ -0,0 +1,3 @@ + + + diff --git a/launcher/resources/flat_white/flat_white.qrc b/launcher/resources/flat_white/flat_white.qrc index e11d6316..b0759d8f 100644 --- a/launcher/resources/flat_white/flat_white.qrc +++ b/launcher/resources/flat_white/flat_white.qrc @@ -35,6 +35,7 @@ scalable/screenshot-placeholder.svg scalable/screenshots.svg scalable/settings.svg + scalable/shortcut.svg scalable/star.svg scalable/status-bad.svg scalable/status-good.svg diff --git a/launcher/resources/flat_white/scalable/shortcut.svg b/launcher/resources/flat_white/scalable/shortcut.svg new file mode 100644 index 00000000..b419a77d --- /dev/null +++ b/launcher/resources/flat_white/scalable/shortcut.svg @@ -0,0 +1,3 @@ + + + diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 270dd009..9741267c 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -313,10 +313,11 @@ scalable/instances/bee.svg scalable/instances/prismlauncher.svg - + scalable/delete.svg scalable/tag.svg scalable/rename.svg + scalable/shortcut.svg scalable/export.svg scalable/launch.svg diff --git a/launcher/resources/multimc/scalable/shortcut.svg b/launcher/resources/multimc/scalable/shortcut.svg new file mode 100644 index 00000000..549c3724 --- /dev/null +++ b/launcher/resources/multimc/scalable/shortcut.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + From bae0a0530bd5334a2f41ed234a20a064b5245232 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 25 Nov 2022 11:51:08 -0300 Subject: [PATCH 161/277] fix(RPImportDialog): allow changing item size to accomodate wrapped text Signed-off-by: flow --- launcher/ui/dialogs/ImportResourcePackDialog.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/dialogs/ImportResourcePackDialog.cpp b/launcher/ui/dialogs/ImportResourcePackDialog.cpp index 7a2152b7..e807e926 100644 --- a/launcher/ui/dialogs/ImportResourcePackDialog.cpp +++ b/launcher/ui/dialogs/ImportResourcePackDialog.cpp @@ -24,10 +24,10 @@ ImportResourcePackDialog::ImportResourcePackDialog(QWidget* parent) : QDialog(pa contentsWidget->setResizeMode(QListView::Adjust); contentsWidget->setSelectionMode(QAbstractItemView::SingleSelection); contentsWidget->setSpacing(5); - contentsWidget->setWordWrap(false); - contentsWidget->setWrapping(true); - contentsWidget->setUniformItemSizes(true); contentsWidget->setWordWrap(true); + contentsWidget->setWrapping(true); + // NOTE: We can't have uniform sizes because the text may wrap if it's too long. If we set this, it will cut off the wrapped text. + contentsWidget->setUniformItemSizes(false); contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); From 81d84b8d0209e771fc55bbcff696d61c7cfb3cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20C=C3=A9zar?= Date: Sat, 22 Oct 2022 15:43:53 -0300 Subject: [PATCH 162/277] initial icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- launcher/resources/instance_icons.svg | 1411 +++++++++++++++++ .../multimc/128x128/instances/chicken.png | Bin 6369 -> 0 bytes .../multimc/128x128/instances/creeper.png | Bin 9046 -> 0 bytes .../multimc/128x128/instances/enderpearl.png | Bin 21425 -> 0 bytes .../multimc/128x128/instances/flame.png | Bin 6226 -> 0 bytes .../multimc/128x128/instances/ftb_glow.png | Bin 12708 -> 0 bytes .../multimc/128x128/instances/ftb_logo.png | Bin 7883 -> 0 bytes .../multimc/128x128/instances/gear.png | Bin 18321 -> 0 bytes .../multimc/128x128/instances/herobrine.png | Bin 4937 -> 0 bytes .../multimc/128x128/instances/infinity.png | Bin 8012 -> 0 bytes .../multimc/128x128/instances/magitech.png | Bin 23097 -> 0 bytes .../multimc/128x128/instances/meat.png | Bin 10583 -> 0 bytes .../multimc/128x128/instances/netherstar.png | Bin 14062 -> 0 bytes .../multimc/128x128/instances/skeleton.png | Bin 3673 -> 0 bytes .../128x128/instances/squarecreeper.png | Bin 9136 -> 0 bytes .../multimc/128x128/instances/steve.png | Bin 4312 -> 0 bytes .../multimc/32x32/instances/brick.png | Bin 2388 -> 0 bytes .../multimc/32x32/instances/chicken.png | Bin 1181 -> 0 bytes .../multimc/32x32/instances/creeper.png | Bin 1524 -> 0 bytes .../multimc/32x32/instances/diamond.png | Bin 2444 -> 0 bytes .../multimc/32x32/instances/dirt.png | Bin 482 -> 0 bytes .../multimc/32x32/instances/enderpearl.png | Bin 2120 -> 0 bytes .../multimc/32x32/instances/ftb_glow.png | Bin 1747 -> 0 bytes .../multimc/32x32/instances/ftb_logo.png | Bin 1607 -> 0 bytes .../multimc/32x32/instances/gear.png | Bin 2414 -> 0 bytes .../multimc/32x32/instances/gold.png | Bin 2366 -> 0 bytes .../multimc/32x32/instances/grass.png | Bin 618 -> 0 bytes .../multimc/32x32/instances/herobrine.png | Bin 1034 -> 0 bytes .../multimc/32x32/instances/infinity.png | Bin 1714 -> 0 bytes .../multimc/32x32/instances/iron.png | Bin 1772 -> 0 bytes .../multimc/32x32/instances/magitech.png | Bin 2646 -> 0 bytes .../multimc/32x32/instances/meat.png | Bin 1514 -> 0 bytes .../multimc/32x32/instances/netherstar.png | Bin 1942 -> 0 bytes .../multimc/32x32/instances/planks.png | Bin 2299 -> 0 bytes .../multimc/32x32/instances/skeleton.png | Bin 696 -> 0 bytes .../multimc/32x32/instances/squarecreeper.png | Bin 1623 -> 0 bytes .../multimc/32x32/instances/steve.png | Bin 969 -> 0 bytes .../multimc/32x32/instances/stone.png | Bin 1866 -> 0 bytes .../resources/multimc/32x32/instances/tnt.png | Bin 378 -> 0 bytes .../multimc/50x50/instances/enderman.png | Bin 2429 -> 0 bytes launcher/resources/multimc/index.theme | 2 +- launcher/resources/multimc/multimc.qrc | 74 +- .../multimc/scalable/instances/bee.svg | 253 ++- .../multimc/scalable/instances/brick.svg | 37 + .../multimc/scalable/instances/chicken.svg | 102 ++ .../multimc/scalable/instances/creeper.svg | 37 + .../multimc/scalable/instances/diamond.svg | 30 + .../multimc/scalable/instances/dirt.svg | 46 + .../multimc/scalable/instances/enderman.svg | 60 + .../multimc/scalable/instances/enderpearl.svg | 43 + .../multimc/scalable/instances/flame.svg | 30 + .../multimc/scalable/instances/fox.svg | 397 ++--- .../multimc/scalable/instances/ftb_logo.svg | 36 + .../multimc/scalable/instances/gear.svg | 90 ++ .../multimc/scalable/instances/gold.svg | 30 + .../multimc/scalable/instances/grass.svg | 52 + .../multimc/scalable/instances/herobrine.svg | 96 ++ .../multimc/scalable/instances/iron.svg | 30 + .../multimc/scalable/instances/magitech.svg | 55 + .../multimc/scalable/instances/meat.svg | 56 + .../multimc/scalable/instances/modrinth.svg | 45 +- .../multimc/scalable/instances/netherstar.svg | 33 + .../multimc/scalable/instances/planks.svg | 30 + .../multimc/scalable/instances/skeleton.svg | 69 + .../scalable/instances/squarecreeper.svg | 49 + .../multimc/scalable/instances/steve.svg | 108 ++ .../multimc/scalable/instances/stone.svg | 30 + .../multimc/scalable/instances/tnt.svg | 95 ++ 68 files changed, 2928 insertions(+), 498 deletions(-) create mode 100644 launcher/resources/instance_icons.svg delete mode 100644 launcher/resources/multimc/128x128/instances/chicken.png delete mode 100644 launcher/resources/multimc/128x128/instances/creeper.png delete mode 100644 launcher/resources/multimc/128x128/instances/enderpearl.png delete mode 100644 launcher/resources/multimc/128x128/instances/flame.png delete mode 100644 launcher/resources/multimc/128x128/instances/ftb_glow.png delete mode 100644 launcher/resources/multimc/128x128/instances/ftb_logo.png delete mode 100644 launcher/resources/multimc/128x128/instances/gear.png delete mode 100644 launcher/resources/multimc/128x128/instances/herobrine.png delete mode 100644 launcher/resources/multimc/128x128/instances/infinity.png delete mode 100644 launcher/resources/multimc/128x128/instances/magitech.png delete mode 100644 launcher/resources/multimc/128x128/instances/meat.png delete mode 100644 launcher/resources/multimc/128x128/instances/netherstar.png delete mode 100644 launcher/resources/multimc/128x128/instances/skeleton.png delete mode 100644 launcher/resources/multimc/128x128/instances/squarecreeper.png delete mode 100644 launcher/resources/multimc/128x128/instances/steve.png delete mode 100644 launcher/resources/multimc/32x32/instances/brick.png delete mode 100644 launcher/resources/multimc/32x32/instances/chicken.png delete mode 100644 launcher/resources/multimc/32x32/instances/creeper.png delete mode 100644 launcher/resources/multimc/32x32/instances/diamond.png delete mode 100644 launcher/resources/multimc/32x32/instances/dirt.png delete mode 100644 launcher/resources/multimc/32x32/instances/enderpearl.png delete mode 100644 launcher/resources/multimc/32x32/instances/ftb_glow.png delete mode 100644 launcher/resources/multimc/32x32/instances/ftb_logo.png delete mode 100644 launcher/resources/multimc/32x32/instances/gear.png delete mode 100644 launcher/resources/multimc/32x32/instances/gold.png delete mode 100644 launcher/resources/multimc/32x32/instances/grass.png delete mode 100644 launcher/resources/multimc/32x32/instances/herobrine.png delete mode 100644 launcher/resources/multimc/32x32/instances/infinity.png delete mode 100644 launcher/resources/multimc/32x32/instances/iron.png delete mode 100644 launcher/resources/multimc/32x32/instances/magitech.png delete mode 100644 launcher/resources/multimc/32x32/instances/meat.png delete mode 100644 launcher/resources/multimc/32x32/instances/netherstar.png delete mode 100644 launcher/resources/multimc/32x32/instances/planks.png delete mode 100644 launcher/resources/multimc/32x32/instances/skeleton.png delete mode 100644 launcher/resources/multimc/32x32/instances/squarecreeper.png delete mode 100644 launcher/resources/multimc/32x32/instances/steve.png delete mode 100644 launcher/resources/multimc/32x32/instances/stone.png delete mode 100644 launcher/resources/multimc/32x32/instances/tnt.png delete mode 100644 launcher/resources/multimc/50x50/instances/enderman.png create mode 100644 launcher/resources/multimc/scalable/instances/brick.svg create mode 100644 launcher/resources/multimc/scalable/instances/chicken.svg create mode 100644 launcher/resources/multimc/scalable/instances/creeper.svg create mode 100644 launcher/resources/multimc/scalable/instances/diamond.svg create mode 100644 launcher/resources/multimc/scalable/instances/dirt.svg create mode 100644 launcher/resources/multimc/scalable/instances/enderman.svg create mode 100644 launcher/resources/multimc/scalable/instances/enderpearl.svg create mode 100644 launcher/resources/multimc/scalable/instances/flame.svg create mode 100644 launcher/resources/multimc/scalable/instances/ftb_logo.svg create mode 100644 launcher/resources/multimc/scalable/instances/gear.svg create mode 100644 launcher/resources/multimc/scalable/instances/gold.svg create mode 100644 launcher/resources/multimc/scalable/instances/grass.svg create mode 100644 launcher/resources/multimc/scalable/instances/herobrine.svg create mode 100644 launcher/resources/multimc/scalable/instances/iron.svg create mode 100644 launcher/resources/multimc/scalable/instances/magitech.svg create mode 100644 launcher/resources/multimc/scalable/instances/meat.svg create mode 100644 launcher/resources/multimc/scalable/instances/netherstar.svg create mode 100644 launcher/resources/multimc/scalable/instances/planks.svg create mode 100644 launcher/resources/multimc/scalable/instances/skeleton.svg create mode 100644 launcher/resources/multimc/scalable/instances/squarecreeper.svg create mode 100644 launcher/resources/multimc/scalable/instances/steve.svg create mode 100644 launcher/resources/multimc/scalable/instances/stone.svg create mode 100644 launcher/resources/multimc/scalable/instances/tnt.svg diff --git a/launcher/resources/instance_icons.svg b/launcher/resources/instance_icons.svg new file mode 100644 index 00000000..0c2b9e20 --- /dev/null +++ b/launcher/resources/instance_icons.svg @@ -0,0 +1,1411 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism LauncherTODOTODOTODOTODOTODOTODO?TODOTODO diff --git a/launcher/resources/multimc/128x128/instances/chicken.png b/launcher/resources/multimc/128x128/instances/chicken.png deleted file mode 100644 index 71f6dedc54f470a6f8219a427bbabbae1f025a02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6369 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 V$PTXg^yBXcz{2z*w#gWm^na^=xRC$= diff --git a/launcher/resources/multimc/128x128/instances/creeper.png b/launcher/resources/multimc/128x128/instances/creeper.png deleted file mode 100644 index 41b7d07dbfcc841eb6dded16284b1a49fe4e8b4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9046 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 diff --git a/launcher/resources/multimc/128x128/instances/enderpearl.png b/launcher/resources/multimc/128x128/instances/enderpearl.png deleted file mode 100644 index 0a5bf91a440a6a2a6cb303c7b551b6c7976307fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21425 zcmV)MK)An&P)1(K4kEr$age&~L| zm(Zuwm%u|ubab>~xgCPsre%_{u53B(_T06YTv0QeZ}rctiE;ID+bRzirtFFM6r+(uZ$*B<**HFO(K zh!bO5g!q~olS#n6HQqX-s=|>_91FxBl`#Cl3c#-feY;hfMmcYl`c&yUWzi|^T=8C+ zdZq0gVJd{6R6#Kl%!FXyA^>7Yx-}Y54d}rr3(vs?oC}n@z_th!MPPCpxGn=#6<937 zAEPk*!3n?!_-w=V2=vQF*{mFGtIQflJ#|bvN834ES>U~+D;z~|RS1rvQ1iiIrW7WG zLLmu&rF)Td62#Ch(1{@u#Hpw2LZj;G)KhdG=RCgcC<{+rcqRwWzVei1V7d0Zdms!U z{T_?E{KGooe=q`Y5BR9G&NG}A#5f~mrLO=)-)T7=L)T7!-2pvv4Lg?DUb%fC3yN*fO z(zP93)lnWi&7@;C^ITsC=5x5@hy(&b zL_B655s!rq)eZ|SX04cOaG?cj#EPcH8iH@JvY`qsp=;@?j%nRdPdnyo&)v+kSOlIt z34HOzACw^cs|mob1b(w9sOkb&mK@XAroL?#uUed5Gs&~K@}1j8(VcnpuwfplWlqSj*CfYm{$ff_i7u?G%_ z?8S3H*$Z-LH3|k|HG~E~Y3aK5SP;Gn|Jpvn?^OUEX#HoODevC70r*3SD@#l!m`uuG zGc%jvm|1Xh)aIDYOqgRf$8@Gi04xeY1z`eG0c8Tdz;K8;5JgZxMnV7?d@zgv7BKM$ z9;1tbKnvD@)<7e001m;}BVmuoPK+HQdxGpl{SMDwME0%99a_n}F`MjXmmq-z{JuoeesX3eDZDFcfy8n(b}t|AMJnJF`D zU;r}xVfQ9B-hJ0schy)-A;=w%7#hWA{pdm2;!d?PjL@Y2Mj~-KoIZ&q96#Z7%kZsKo1y)!~$C}*@10>$Odd9CL0JFG2P(8R)TI@ zl|2my%xb)ESgl&N+m6di&kw&0{MGMI2>wL{;0XNBQC@vIXuWC$y9q(26gDq?F*gZI zLsuXxBdkD96j>p*w7?Q!L8Si~=oD-sPyv;Z?0p11-;`s}@7wjS_o?~-L$UT(a8?A1I%keXa+X;JD+@me{muB z7ZHF*ZT;=7GG73(8R zBFWZA%6ACS%g^8&lC(b`&-niS-*Il=dfy-lfe@mP(6d2@(F(LcZBQdbArRYQvW=kY zT@bqi?j+bPrfb3OqzIcf=oTk?4v7&?_AR?r$LVSKefS2ya{>5PM{hDIkxhl^1ka43 zoENQMQOe3pPBBi+I8_a&W}HUgWd*jxYymnGY--p9kt$O?y|zot_8n7wIt~-inECrv zH9;d~1_L1X3qHv-NJzdxzI6yKW{n`VAqR*i0c3+&)PCVM1icmE7U5RJZfR}Zm2OSf z?r`jpP-Dx6x^90T4&v`r0PcaWynLzL-a4k!5}05%r3mxVhlLe#Vx63zoSE4f!kNNZ z1mAkiH<7s-GlS&aTSX7NIK;*ppTHJD1Sy<|bBJ>yg#+hAiUKVhQWl7FAcFaTdEejY zyjj;_oyWS)LfaW30A?ehNQ`hK3>`?5@&$De_F}fnOaO8x!fk*Xj2jWTA#fvw-AQM6 zt@9}`nq5`jg()78LeNGqANpAOgY0i zH?cD{&doSSI0a6CCCI|CxeOkDnQjij9fO|IdPN~Rn~KgR;wF=LG#cdrByrO5S3~|fCx)U=wC2(%4=ZalK zA>bT(@K*+Nff;>hM+s7p!#U81jER<|=yakwn;>P8-pWU#AT|KW2;g|MfTp*`uMfbF zg&+$?Xj;PUn(l5L!giln!A1_Fc(9Z^h#{lFUSJ2Tfji(V;^cI8h zCS-Wh3&wpxh}#zttn>K0HQmjf`MXU>W+FL?y&y!hAhHj}PGCzEf@lg<-USQq#pFFI z?(r^HSQC1vxdfbBs$< zxm36` zz-fHX>4@Ib_d*r`58e<qmQ!#ISDX=N+aWIzZ;m9UVpf<)0Td?URB!=N^BN^ry_D8Tt1;z7m6_mE;O}4*9sD+bzm)(a z?T@#YFO})F!_P`;o6?1OS-F)tJ2P>YX5mUzo|tiEaD~_fa4N=$Aq%pXZl1yvKY1@1gZb6I1-whJN!T^DyLs zM)ETP-4gkXU$-o40gym(c}8(@uI(@1TKoDNtn0EZ%0($P#EIc78X9MY6Hrk!SP;Ys z&Qs#7?R@G_oEfJ(W$jXpK%;j6O_Y!TTmX#K(#fXP((D=e(g@P+!Qo^LdW);jP@qRLa zX%*jdF52j(jX-xEYdzAnNaI19SobjxUa&5)fhCiWox|)W3W#QLh&Ky@NbW6Rupnc> zP$-{YN%`c`9e(!r=5OwDW-_v;h+5Fvr>ztshygI3&RrllE9Zsurm=^nX!Q0)6dyF zb+d`O*<6OG$}p#pDBdO&OeffU3dpc)lG!dd*G71f4C_>wXfT%H@25xWRv(Jn#zqH>*2!0zA5S_kXq>7&R z6Q)cnbITCy)PkLx>ZO`Q%Ezw2EYxK8YzBP!|euf8u4=ubAkke&Vy@{ozEH{qN(ty z9Uu{Z^Z~*slF-BkBXDz+kflWcS~^R8HPHnzq8GQ(C_WjuTUNhH;76 z6_9Rr0eU88Ck6|I8Aj54aR^$jmMX3otgcG5>y^W1GwghUU7aJ(FR|yB*wYK_a)q6) zu*EEzg3?86(RQ#qAYF(|P!>>?k^Wnkv^0Si0{DQ109`;r*GCAEh5E#DqWN4RkXUwb z1Go=-0F5tN(0f7t3xGGp2kSf?9@Btq0w-PI z&C3A4xj*o03BUvD=XJrmS;@ptD8jr9x(a5em|d94r5cweToGgaGr?9SEZ`V?ajL~? zp(2BKjZ1VtiyAM_&XMmuK|g$se)J4|dV!oQu=$j(DrlV&vdL(=sQq;Vb&dEg(@6p# zV4aUb(zVd~{wBVQ4q~vFALv1m$P||By$k*J>>l+W`2amTghz$IEf&oF%}@03-+o5e z9T=R?9En1z>w?ELHl1ExTQUW|QGVbzF#)lY=NG?l6!QY{ z6Fjq$urSdR#Lf-7P`Het9_JB!Ss6X;_uy+;xMDdU%55pBLRAo!Gvw(7`n_iqKX^g; z$@iGOc*^qPjQM>)l7_oP9 zL&YrB7z{eax*)V7v}V$lKC~@#hWd1 zEi1~&lu#_pTa5Qj&sDfE3w?%9jFn*vkX+D-;8%q!mJ6{g_1WIfO6+uj```)gPd;M) zqfa>dkd2Au-nJmeh>Qyv>Xnw-6NYlY|{Pi zfE?=how`Na#2h{~h9%WAl7?FZQA{yM^dppLLb4Eyz9-RY!9&6!{!iyUhm*;3wIm1+aY(` zCT937Dr2de3+j`4k2%;(MQ$xH)pB;^_r)7qgfR!Wa7bJHdewPpgW%4JV zNI3jddvkzHK+Md7hhyc z*Z$8De(^3+zw;HrrezTphORKqOzd0&=UMX&R+t^f`N`ocR!arJumSw}1iL!N{on(Z zFMq@bfA(Yk^rMgYi`5l>RxbEhCR|c5LD&*lTgRPmSl4@+-5%Q>knJvkze6?=c)8n- z?qfJ8yF)Ss?cfWTL9m=tO?3d0F}4V#X^akHIjaA>XfpgEn~5}P#wW-x&azLamHPmeC(+^qw#h>!Y z%b)P?Klqscu(;x-F8H2QoZ={e2IH63^7q}IpC9gcvt83{wy@cy+a6hOWB7NsOZVF( z6v-?cYOL;%woP{5r#e9Pz!LRmbW#n#T#Vx+B(FuIp|=oXw5|!^ASs3OYYIVrj-pk` z^v6HY{ono_sTR@;fhH3GG$zz)9D>;%lLKB46xtAIDfS)n&X1yk-_8UaYyVc!MZv^Z zR7^`B=0S9Y*(s91w^+{?SenTUv6SA8NuFXcS4s9eonoQ&{+)sLP!b6MEwb+ksBCM^wAeEN6Hpz)Osam6cgC->*2Q$0>c8h<$}o%KGOPU z|I+}M&S5ZiljZp-b8n$4~MqW8Yx;fla8?Ud`|dPEbyxsV9@@9E3s;V z4|9{S#O%b3Qx!>VT$~6j4CcTTlQJ&Gz`5x}bvhYNuu~`<;dF_7?-|vff6DWhKjPoL z_=x{~e#O7n1urO>6I=W;_`m3O{FnVLe|z_qH@EL;Zte){4Xk&_Z3JHu_-uC32iP63 z!vPK*+NRJiB#%F58%0tUW_*sK6A*pW5RchHVhc@qf~fJBBH-tG8u|DG>zzUJ?*f64ax8h^X)DWBUNa<_@#uXo5c zI)>CWQpZrx+5o{PXRjCW5&MrTPmr5N3=p4Ec_}`K8N?(QcnJCc@P|V12=yQN1J>V7 zK6)Wxe^7sOn@vH9Ffp?QNDUm!*r~D&$QF}b;cVXpIUufKGi_;Z!>?k2?_z+c{TFYQ zLnx7|D!t9jbb+vf$N;FEV77|duTyM9{i0H=mO>7tMB)jXO|Yvo_`&y>{Nxip`0zts zE-rYPz@PQt2kwIB=iQe7XZN1}>*h88_1$aUy?;k@a|<^SaJk(?>R)e>bpn20_xXQv zm={9tbj7q=5`ex7Xq143-Yg)3X?#-x!)LQkkP$W_;8~9D3xUPYJx2X~;;(;Qn11?+ z?EdptVx5ns00L+y*utPjWoKqvgbm0BFWb_>zAfB=;?S`49dBOhcd@_&0r(c%U*efs z!OTKf8m!dx#NdQrt3Jg$uI!13o6i)@;K!3gRT55?*!P}N{OBW2KK_s&oL=x}#exr| zVnIAvjRkH)%g@^#|8e_{|MU8v_%Cl?^ZM-@>YH2a<_>N%_}d8n_K?7Dps8ckuk+wX zDc(`;Au7BK)SWAjj0`|B4~fI`Pf6KgiNzq*LbY%mcirJMr|Eaegm zBo_=wfru*Q2hX(mhu7KrD8MELGcy*cpJJ;f>p)l|Y;bG|>~UqyX4?KLU*OvrVEvhJ z^2*T^1yogOHZ`vcgeBN$(S|G(OKsbIufB?iy4f_H<&-5W95$W6lQa1E1?9&daCvdg z56T7Kr{Xj%-P>E>t*`l6z2!e`-tvE6zvge={3Ea5yyI|vjlWqVw;Sv>#`n?I@3Gw> z<^@vr&pHoX7eTh6^(&tGQGq85LPnoK2neaEGyxqmg@as(Pk>3%@*}O!`vknknrAO4 zqb#CYUo7gAOo^$gXIIjG{YI^6EuGsEn5Zy|{S>xJSgb+Tcs3=rYtcPb-B5KM>(4xV z`6$faCIG_it-{JN=}Npz%)-nZbAzRttPEBN3&ZAEPV)|(yKXsGG&xR4^!lp;J6&QQ zT*1fBsGeMKIgOfsi9>lFL%_ONe|!#K6;qt+kMe*SR`v_Ir>LbQ^1f63hB4FoCApC1X2fC40MAamQuwQ zFgIcbWT5&H6GbK&_GhG8L^cm1R3E+=qJnsiD}$+t%>@>itQ1)Vu_eMBZ!=7HxUyz9 zYvDE|9e1()V1f$Pxns@?H7+iU*Q?Kgb&?i>F8?Jv1~`;NoA8|-Ec*X!u) ztz%Y?ebV|mdH5N8BY~gHUvJNQ3m?6HkzV5v%YN9v1sNOli3w~ZAF)56_Zw6b6C}p- z4EA3=1lRLV*7`oBtU9nmPAew!P+XnUzW$|z{a&LGBo;7c(ec)0#D=(%%CWA@rp>|a znH~zJq2sg90$+Y<_szg#3=ju0ajqN=1=3Vz<;2X*5nHH*r6Ehi=Abi+c7N2jFH$TQ zI-Eg?&6-si8#kU`Vjo?im#0k1ig^$O&o8a!O|#?w-Mr(kufO4^@80nByKmUOy`j6l z!`|OSt-syCW((U?_^BIcnh1P~>!ZC-K=s;XJ@X?;5b`3TT=SIy`W~pn20O0rPBei@bo#QR7&Ijqb8Pqs3*>Myn+JpHqYw1()1Qr5pp1&AGtA~@EEQXt z=)z3qCQOi~;xMUUs&EjFVxDgkfZq92N7)txt;}qOGBdM)LR%GpGTEW=~_TmbAa*52QC`Qw^yz*QAslDaDuix|4%^QCH{tfTny{Epu!N0qO z>pSdbjofTve81g|C_h#Ed^*{dwSRDYB^&IKEWoJJN4oWV-XA^v{Ei$6_;G@W7`qc4 zLR=D-Wb7qVOCRp##sQK!7+9bOJ(-8WNsOOyrz_lQDSo}xWM)f{O0cO3b6^3wFl-^p z%mOn+D_m2sH%ADXoWY~NUI2RfUwDRO9tFVPi+9-WUvmY-%my?kWon1A$j%EZ(efU05DC_ zL~i0r_tAfhiK7Rgn~mhnh9xCqr*Onz)rT*1|6l)m<{1}#0h?el!)tX znY3DzRCU3^+Biw|%cE%EJ_AJh?^cekbC^{)o0wy2Fjr(@W^M#+9W5ir{-P)7`$;AwqbdRSj0Q62Gr`g;_tvEjszuv^2w75{V)Ho|)iZF}pY>A;|f_5d&3R+X@I*VKXr=!Qh zXP?D+yR*holtmuN229m-X0e}dN*u?QVt&c!U76B{~mmrn}w%y-ZwFMsU#^$y_p7C&bpJr2Wb z1jD?g^#a+6OrN(f)D4n}u^d{C%}#F$EP*;QMV9AI4aZ{rai+x4z2miezrjYo%bl%A zIcrVlzKB@J1dCJC)U=!7vXW8i)7@+U@I~*j<-gQuyXjz&uF|(Nr zg;%zfZ>E5jS<_$J= z)cmG}wuwM>egt-};z?^p5&xJNcm!q;qsdVRRM2O>s=4=11*UsLWEGf1rQu4Zmdi7bpun=0X4f zAOJ~3K~!Dr%AHN1DxvFQQ^wskCUUm>7|z}0?wxIXyor@OsSgSKHWoueNYop(tPjb0 zSr~(U?{RALu>^7s-^T0wtak)QUrTQja`8t-)IG?W9PL;7_m0_!!S;_r!Qr*x_r!AZ zebXisXD8b1_MtZeMxk*|(u6Do$`o%?Q(xhVf}(TOrwa8^I5?Pq&xs?~yH064S9xC$ zCvK+6oN+D>P#HtDk3|1&o)SO(Iy&OgEtl&4RRw0qu10QC?LLFQNxA%Ws_1QpzCG$L zg73$)o$u*Ad^3g70D5 zW2!$s)-l!h&+or)hC3ab0ZV4U;%ehDRuU?Zs`9>|?HpaFbX~0Tq`xcx0-r`Q7A(3rVxP|w=1}zZf4vs2yl_}592{B{ z=DW(!N{|VJ31~&E^+(N@7)-hOJU&uMW-(>hG%esPiueO|2gHXXEhqQ$r5e7CI(O(o zTDUO+?#SLhqJs~FA>WN&KjQ0@Buf4NV-J0(>oR=qLCGk7Tt^bod;TNM@BN5C@&`tr z&-sCS$NXKUz@45*IJ`}1OtL8`!78u`=)|xJ?<>)wz;_NmSFUa)Up_}9E=!KT?r;#e z68u52!l9u+rNojMu$b}F@c_n@{L#fsdQw!2;ZODfTug#U)5gY)&JQ{KeH{g1lN`Z) zYT#(QSj7weNCO|7^`pn56PHeY-*!IMpbVs?&cFUr0oezL`2p+O!Rh@B$yq#-6DaxQ z+ho@eJG%C^xC&MfWsgQC{9<>e)A@Suf#L$;=Dx4h?&O8={^*M!XaMq zSArkX!h;i7*KuQ2=kVeTEf6aqlmva88>^pA!d(n!K(N?y%g- zeP|*B?DjFt+s8V-)QR>p_1`~H57mD3epdH;;D_P$kumR`M(LxL!~}^E5J}pX1!crF z38V~EFBC~zhe&~Atxv1H`gU_mB4qbi!G=jDgHQ~=UFXwOKneo`WPC(Q0%e?-uBEBO z>n1pJDHm|h2lz{d3oCiARS1y*LMV_lbvq4X9JD_rO_)#1_er@b0j7*9>rnMtM zi`x$m_xITIkeX8OJFMedHc{i>+8>LdWs%Ri5VN?>s z<2NG%yh#y2Q3zow6hR3%#F7xCsoQBKFVXW?1UVkkl<8ED;$f+~Nb{|ax%3dCrf(0( zejiuz9vYakN-4Z&+mz#>522TAx`aKao+bDt)Fbpl;Gi)qZ+f#0@J57B83)#8Ify zsSvt|^_yNzA$f(c3PK3+8au%pVopWkynSPsx*c&45Rt+Sf%EtW<`e2IX7B1I()^*0 zS-o9mfmF_IJMbM6BKSGU()ZyH64meOfjKw=iIEol>TKIFTZG5oAAj%Y{ZZ5~3Mc#R zjBb`*AEE+|w?F=^k_5%2s7ms0iuYO+$=lAh7B$RaDK{WeAm#|>ESM64vdULXidbevKYDgQI5Osu}<6XlW4V@(4gjq>eUsM{Fl zHx1NXa{Puc%uI-to)NgcCqB}GUQNf3$JETdczckT+^jSX2>N>&Ib*$s4^%D|!Ia$E zm0QG9Yj;WpkXz1TNjf%>MorGnViFoj#f5${iKa0)ahtI&5tofi@=>HH#>bq77oh3d z2o5s`>P&-^fKo{`0nb8?1mw6Fcvl!IqEisdxs`2)JUszq6y6!GzNEU7mtCrEO@;}4Ga*?#c?EYWI}Af9I_Ay zaeSQiA=)w3$84m8*c2M)Q->q=>&Gc*qW_S>b`3~yF_uc(i`e#(7sF8}SW%>5SFUHB zae0{14b*Fieq;AZOc04WvnWCoF=dZmN*qxb{{{hQeY&8cMVn%(ILjAVPy#xNIa6e0 zlBF=#m`AE)NyWTC=aU4d?%b}!>NeH%J80syneucFu@QqLJ9y-P9m&9OLI&j?C18%2 zB5!wue*P(9LvCIeV(%=;nf7Je*q1asL<}U``kYf3JmcsaNEmtpvc4p3?tfSc=&Y<_ z6}2c+C~2t(=HoZsx0MrJ@0;3CPs$1b5EX=D%grLlX8Nr}xT$YP466uZ>15-Ocf z$0U&fhQ9%u9xEH&AOEIY6&$wUN0Kqj)z58h_r5*aYne77QQG^~dy-xAL(!uLx$`O2 zn`pmFasN?l90oHoQpoQHcJy}?DyHS={d@O5(2H`X4yL`fk1t7JH$!N@+ z#Z#G|9|@@q&vO=16^as@RxqCumUEQ5`vLwtV zapvJN%|3**S|~G0+I9eIyKg}y?Iq+dOWb+huhV#C19Bp0I(-D-zQrXO^j>~@IR*$_ zmzrv1IH5A4(UDeLZm;q~eLk+#%>pnQ%g$(EeXb#SB(+SS_%{44X_M3#jx;70rp= z7k~kq9xEB&AO9uHk4=E4QXXP>GhAAv6iuB4VeGQRje373>as4C9}8R6FS( zCTPh*&|jNsED(|%DNCXGJysBji)LKznwnzE8Ma(SA*c$d+m!raFpn01fOWk931JWMM7R{?%bsflOoHq@1kabU|3gYM=Nt#x~H0$ayuAbtxe*I6EOV zD5k-Z8VDAU5Kv3_sZqLK2#y)RZ46%+U1w;za;AlN6>ThD7bjCpwkWNS2#&nd7!lA9 zSRDQapu|$my|25=0BF+iA!JH^gvFB(wK(*jmt4_H?btEhD$yY$rE7)wJXxGx9`kV> z!#{v~AK)X<^XFvroq-AZwS<{PGVM$98(eOik4~SQEa75_oGoJr7(y(ROuxMpIXD*wV9zGVYJs~rp*lOII$coC zr>MH9b3VjEnGdl^CP&C!H!`D%@*!UL*ofGPc?s%K4Kx~1jaaW%27N=w7r=+GR-@1$ z4sQXE;e&-vL^{9=i3uQQF)VbQ^}jsZ)vR9GGam_rWgt?Uv}IcCSK|yeISX}6UM0J) zVN@Z^erm`WSS*FOVrl3?n!rW>c<={$eIH?T1o9DRBS9Fw-xuQK-uII8C%x9EpXt)h z)J4Z{R>8>vcX`6}>YVE86kRQ7fWx7s?Ly3#^n)%2r#Uh}&L~9z5F+S67tA_AJt{sX z^EI%}Yty~}vI%&C@rxodfF6ua1BH6Lb*OZhbeMH%!--cV`T}itq}37o0r=crb#$+( zr9C@>E{ym%JBQisN8lpBxf42qE+GzY96p~l&A1}D`lP0aMpMbXKY8SZ5mU)M z@Z(P&34`Qzvi$vI>-*T9p-aj+DC4jUSuN1>6ROJ#mRDyi&rXoZgiX`XnbEbWZqjtI zu$IDSkT?JXMzl`=OIJLX4y7Z64kw;21T+|08AV|{VH5@Y74T^hRm!>0AifCNq1Iuc z6{EFy3H44=1fS1#XuG)Pd>n`Oo_wd4fkLqGaX;rbEya<-tn zJZ1Uxf{V)wPF732C|%pK?L68xgt~#Q#kwx#0OAQV-(|W?+8<{MqtZ&S*02`Ujv!s5 zc$@@6GWCBIzd1O8lS#B82lJSD(a@P%i)o977L%6#GU{X|FhU^g4#;e{u1^$*K{H}G zRmd5#=!F}SdrSIIE;*0}eIq3Cb4I56RDI4Bxn76{28aO*34Bfcc)9B*3nnoSKYqPO zYYglte~^8I{QhI1!-H+*eWr)5h{c}y6i!y?&bSbP`5CvKIhjIyKr?hC2;#@JP`S zx)!X#w1LoowjtK|22+4oce}P~K9xSM3mBsdR)#D9vG{KB zs->n)#LnUfS?xSb1MaO+e%SM^_TxfUE90#*%3Z)YPY9hj*GRnRVGUY?H3%(P08;av zzrC{up8nCvl99Kc0LmV{>fqJ8!K;_NxJ2^jkGKEeLXP=09rica?ZJLO{9f8miYlZ( z#?iLMw~_uoeDH`QO}Cat`Mw=#vYkPtQ5Oy5RZA2~Vd}mQHaYU`<2V9|*fR z)S9+Kd@h!O-|YqmFf)MAf;0)MTA&fs)44k`qYJ0{AePpWfM|HwSsNYxXx{bTE!wbU2uvy+`lga}1B-|7 zWf7genD{EDaNr@WZYxhzUHO@p-_ziSAaePn^U9U_1L)-_n$#a|&Q z=EpjTcr_6-YB8%ZYsIX^hmL&_s0b+nhz!sl>=#7uuqXm$;Tz|=f&QrlYDhvr_6JgK zK*ap*`cci4d=j!&=D6G?ZF4fj?y5uVKGtHrx4n_}-q-qUeg?o&ejsc6sIDNAjwfAY zayXWFT=_x~!|i^3VSF1ol37Sp(*oHdE{d)5<2-Rpc(wJ;K)6I z9{_%j((l{pq>a5ZmxU}Pn?#aH%2ygyHwcan`#S>vI3#!oeioWCt&Cno+Lxyn6fdrr zesIaf(|p6lq)SL%&41&{bomVx5jU_*zS<%KSg?Gg#95TFFI3y7kX2ja0(m{ zIfxYvzU!z9&*U~FXa86LdOu(~HLlCRE<>&YX3UQn;L9(KstW8Y zPuF&>g(lJdAxPMZu}2~cNE}<*L>lpyl_3A}3z5 z*7jkT9toVNK&JTwG9yQHoHmw`sga}o-vd8y(@8At&L?m(hl>+!EXUE8s*JXIC)^#wEN_d9}&#UjvkflfNiT5+K^kv*^jc9{tR)MB*A$P8THS@Gmj zdTrB`q{%FqO+ucam9v2WsZcc(eHbNmi1EMj_;cgP2@Kf@$e^LI@gyHZa_o+swwzax z!Jg;hRHFR!!v?kKeT!^TT&&$!6)dK4F4FlaJikCbctZ8jQAD^!HcvA5~ z3+6$%={&dl1GhJK?B3nd-KFU%OiGdl<#ZF8A32) z(>1(bueo{qp8fk9y1OlQh%2Qnwbg~DA=FJS$i>8uSR+V{$sT1Ff^7xaN#KA`GqDD% zJqz>j!tVW;!30?Dzg~T5ysHAuq$6}~o2Uhm9U?n1*+wa2FGvm2&?kn(npdyGL;F9{ z#>f1FZ*}lS=HanOU_|{#-y2i)(d#+CpvQDAE^X(yqIl#m%4lDb^A<_t%QOgcHpNzp zp7QzNDe~cSitj&X@!@lxJb%K+XQ%vVw%~_F#Zz%ig0Sy8-fj2%^8GcpZ?0*sZ?WAD zYuclM75;9MvS2y$ClP#!njf)=I3XNbtR{3V&7|X96<9_3|L#$-Utqjk#2p-@Lz+g^ z?Ja8k77JUDEwD4>05&6b{uf}*uQ%dv*SOPV-xl2~QclKX({Mk3FHt5%!x7aFv-EO) zpVaA-UNaxrLXVer$K}_Wd?mfFzynKZe}GvV;`SbNA17hT_f8Y##~yxLEa7Ycm#6W% zhaWuY!@Vb07kqqr!jEQiJ}oA^FkykP4;|Ns17Ba?@a~uI*}T1`x!n+Ub+r8{@e}HX z5GRJ`xW5H!3^`!7!`MRDis+UgdlB}8(9l&KvuRY&FOIeUaRC_2KmhGHOd63;i-euQ zHi&E#Hiqo5uoq-*Foirh#I*T`e+sj+pXiwC@B0Hs<^V+`3`w2Gs9#2aKPCjZeNhuY zmTnpy!Q*<*=s3tJ>u~}o(SF~cGB#(~*-NS2No?gJUDsHy&fv*8?!^_8=TBHYyX48` zIUk>__;fPkQ&;h!I8Mw6!MN*MzS(Yg{q~x-U%zGl?goFCw7>Ixn!MX=Qvog}eSlV= zHo_j!orrA$vJt{ogq@%@Ry9oPj_E!;M*j~4z|2fU;L9%qpMDx`%(QD8uZN;zkCP3s z#$*GwL1Y=!jP(w+4}cqAj?2vsEBt0JWJ+LS22S*=xKivuU(vmV9Mnw^6ZlG`6ZJVmz-T) z@bvtQk5?;xSWft%EBPLd)8t-kx{kNIJ^%FfhHt+9hTCu6Qop|?9O^g>GnIYA;ed5r z%8h32rw-8!Z!wEk3=vy{Z1A>k3OO+KE%mgc#`AaaI)cCM1tW~p(?I*m(-bXI)grPd z$PQr>u(e=oOjd#{BwjG08dv~nUcHv`@=PsjOm5g1Yu^zINDoSHE;8ufs`V*F+?$XD z5F9TT8?Ef^T{`Li<%1dALK%N|e&+(&(1l!IOE0n z2_Mgv{Gh1#*p)mt;RL}MY`o{~;lMw>zv0#EuX+3G8}@In={CDG7BBTkSio;~eZePb zeljj0ATf{@rzG9svQ&Gv?7N>!!A>2#S5!du7y}d8e1uv!*%x1{R96LWju1=AwbINBIOrBkE`s9kM zvlG5|vf`uZgimzBhg$GNltqYh#CO5-u5S2;+gpC|`j@R@+;9zw- z-VO|ZtE~-o-8(z7%gG3i$8b8sogd>LoKe0%V|+eic0S|%qbVPaCVbfI^T>PN7tg7= ztcw7ZMy{Kd=Zghjy?Dj5Z@%O5*(0!TP9T?o_E3D58VH~064?~51uGbA1S?FOW8K9^V{SFWC<)pn3yK* z0E0v&mzc*K;4B2Go_=k^KYuK{y#EJ9ox>pU&Aa|S5bSOYVOIf^n2Iy%4nPn!6`8u; zgMAv%L0}JrjuuQL&E}Vh>$ofI*h${?4^Ai_o-%lF&hhyP=O@Sf{%FES{UIOulHa+4 zbA+i1{p@KfRj%5W=c^@OzIesgfB%jb&z`e-bwM*sDop#fC6wxoVU@J$a-1eSy1({zt#t55i^_yfb>&^LO|6KMI1J5gi7Chd`hkl6Bqx)Gn>N zg#kPGJh^CP&L`vd$|Q0%9b(55ux>{gWgT;^(9%8~GR(=BmsEn~T^oM7P0Hp|N4o1mbz*@$)zC{x<^P5DPqbqB$05>Bk!HHxlU^ zZYW}7#YQ5GFa`z#fu4a!%qLO_r2YOCZaDDegVWpndUTdJ2n_Gm2=0QQ+x4C6&p=3S zKD(Gsrd3EafUaDaQ>xB4*dY`Si$!ITd-jINWQ@)x$b%E)?3Ci+34@2H9G#!=V0OZz z(U^}013oHBelI0w2uC>jq(C$R8&xjan&+z}e|vSo({G;h{Of02KL3vD@|w8bV!7d? z%lTtfcNaYoXDud`z{X$=ED+{u+=#e2CJQlL;oSy_HDS=Q(8wQueE0t-0Pc+inxdic zRoio$BIwebj8KLGL)$Znmm*eJ(htm06jFcnOx&>V{A7H;>+b?UKwQ_lbq@&OP9R7Q z7&AA~E(SKNy`<}twfiGPNBBzm(&iXQsXHSOMajNCZ`3dgfjNZdDM&~n5Pmg*3 z_=t}NL;lbo@S!VsUpyz8v|EY{twxQNMK0=wXUjQXzP#YeZ=Un~%fIvb+4pQO=7jYY z&B`Z&V6i2v52F6mfmMsx)-3IOU@peB880rDXlU{1ObQ{`wWe|M8z@nU4nm023BTL_t)} zm1T<9-{AUx90YejF&8XL>h`daJCTz#v)DPJ0Cm2T9WAc!Hgs;Lc~b zhco&Q&zPQ{aDH;cqsfGi2kET;y?7poV`@TAz*!2_D^->;^19mc>}J7NuU_-zH_v$S z)wf(ce?j&7nr5*~=YG^=d{x@TS~I-6CeIpUtFRVW0ykn@tK&wH8xfmJp({e%U`0(B zwp8JrcK=%eaLWsPb{66L!0V!+w5nCVE}Zod>5IvLq!!*MJ%>_63RCl`_?}BMZKV3| zFByLNXSbUNyyg1e1q2+pe%qIg>7bU600E2n=UFNcbtF&L89(0#SW!TKfK2+>(F8l0 zV&|!x>(6Hl9-eS=KI6TkBmOWN@yDXiAAQLO;+c^YnhHyu&s&VODc5botId|@*Ef9g z;w8`i{yi_h`Ih;&FWKxadm0jdf`CTCa;;c*%`4gXR*91}W=r4(;~L>wOs)}bz?QAL zH7-;%vEj51@ImD9*KghZZw0_17Wm`~<$wPvGM%@yWsR32sH}=;J@e96lb&<*%(RC{ zX;>kGw=|*I=4gMw&7~O!tt@KZm)g^24B%+C20aQhNZb$DX zYID3HNTy(O`xfo&@@;mEouA+yp5PzM7@wVTa(cw?rV~CMj`*bL^N}lf56{$6z+)zC zOsUM6$Ck^w;>BvscURYZ`|V4e)p2s-@at=-7B`QuTrYFsnVoRSH(0VtTv`? zh#b0osB*-=#JB;jMYvMaWPEiuf-Q(~O;c3(QNua}{`e^Jm#6Qv``-xwhgjh8W8<^W z(ot~PtMT|2SCnd{8Ksz%nna8W6ABb>Vy?T5zY7Ab{^|z*`ALci-U$TRnlA0=TY+G{ z>ey!po#pd`&I9v14dZ^|;j!Zh%%<4c40~`4=O@Vd3FU0cZ!(U{-$hP>|!X5uLg zYBOq!R57xOfko4Dz1?uNTJ!pP&c&-ME?>T6@!b`hSC=$bbHZYURc#{4>?WL5X_srg zz4KFRmq6P2CO0^)#kf@DQj9CGYrz(+cWcls!D@~-+4VoZ_tNK|0zmcxY5#ojh4SgA zfujqdDQa5hTWNjaC`?=-riBWHL%e1FT0=z4WJeE>3+=k8*#6)DX7J~aoga_i41&DN zcg-IMKoHBdl63c+E|GQ2RG3R-V-XFJIs{g2!+f)4zFu>4vt)5KXYu-q#p_G9udZmWZU{GPbW>qC{B>OT{F2>fE}NGpvnl|LZCJk3V$f`RV;2 zc$@3*T)f#XC8Wbz*OBD%c|YjEI2Cs6bc&tNum>l}{m-VjqbW8@B#qI-R+Ydn!Bxv}y?Jd!zkIgtZ|H z&>N5|kPF}f;R3TuQEr?s7L>N8l?t0QEb7Ck|D9a_KMa6FEbtiRv$M#{m%?CBQ{kFk zbfvh=P^n&VF7Hvd`$o&YU`JQfu`J>1FaK`3tz7@n2eJ?Fl5w%l8@$2w>+WpXgJXB8 z12{p;GTq%zQo9ysM``CjIL1y-u-O(`ITL)LFbNJLhrHP-E z141)1>zMO)txf&>!F~JvD|B1A!6$zZy!>#N-*Np(m`QKP07m8r$OR!MSmBUfiH-+J zzV2j-&89G$z;q15exhVGg}OoOmKIpG$<-HCi_|r`uF=hwxLm`sLg!0#xx(ffbeUAn zs2Z$pkeGE3Vlq6Ygyq__ZBv0~Kg?N!RDxyWa|3n-b|LUevDbpT5addN&0FG%zTZ+? zO;xoVA4k6U$6fzV0>BSI@Z?G4(@$Y=-4J`APTD3)>AVo5AYRlo^J7Xved>XXf~h5@ z_nZIoUs8VXo+}@o3wOMM9e^SggSwl({Y!E+&|oiJHz<+802>Xl@gPar z_cF_A3`w`3$)wCti6NqOL)bQn)Ox)|)=lcwTP0aLSy$L5N#EEuDV#|RNKlg84PZvB zYfHC!`C`ZR6+X8u9OEkOd@dAwE#fZ3;+1&0X+vDn^BYPk&YFg57J2fdn;89v zjsKGXZ~%hznvuL~VG8C)?F$>W* zfB%m54t(HP(W1KrmJOgy+EHF6ZGiBlxk2I_#xHjQC-UBpq-C{`1& zswQmftYWs^tDIB~vTageSGTE--_hM`+~xLKH^lWuEoz?P$?aT{W-cM!>6=JS_2f## zE)^~;av?q&KXTLhxTKH`g;i+PvYJMo{sbBv_)bj_Kj#rV>fIMAo#f{_2^vFJ4RU(fiIHO`#)&>H@=q^soCs z`$-`3un0LL&~)8eH^@q$X%X)ebBS4U*&U5-)9$p5B-7ek-KIJHy4~>|OQ&yS2fh1E zAg+Ihz4zCUWMmGc$JzHUmz8 zsldcwgpf+gw*;yZsh*#XrT6>yof{2P?D!^Cj{}~8RO(G6j5-*=uuSBOz1-HF&u3J# zENwvA&I?3n8zjFcg6lM=FHsW@@`2&4X$h;fTGQqaBi(tQh#Q0&qt3T_slO6#K(0mf zD&_fh30#WGwP5qsiX}y}`GN5t^OMj0!}U*jfLrmvW0cPy!SmB z2CNZet7*b2UFoLU{*5>;)wmMl3g@mwxe>>_)nY}j-LRI5erNngk;hMe7URP|=>c{> z|8na10y)Q-hJf5O#frxyfXwS>(DC z-i?P+98H}&p5l5X`>tN={>ycM%t+RC_LA8NR8#9&x0Aam+(To>{Jis>f`x!?YP6~~ z4Qh5>r#se^UF$a4R7o|<+K_&|laUi*Q=yyMbX%FV zZTIb+G1572yY#Njui8>gKX>ou`+Q?A!nLMby&HsUQEmiVw5Cf+zov0pET?+2S>VAF z{ke?~KO+F(4hX>U>8FnQ+%p)IKp(RKg^tRoV=Lsyf*j#EF|!kd69*?|9D^N!Oc5KK z$jGI;mwq}Htj8`kkWRk-01TO5*14psSC+WKCvD@xiTEOoeHZB+Gb{$I324atyD^xx ziPqj?Gz-D<^>g0sheqF3^YU3Q+1=;9eX|WFYp^9qYUAo$zsL>BjTE+!kZb$FZ4jw| zeV+g1N%ky1ukqn$1_1m31j1*ZIW8|f{eFq8dYJ1I8B&y^qK#8ZnVQKl#<3a4&gpRi zflLvZf=w_RgBuDqFszSA58El3dO9#yhw7( z=v^JZ#nPZ=8ky5AB5ROUy34m^>fv*95#|VU5u4N6GL(KvXg7Gah}PJop{`rb&JK$_ zKfCea7XSdf0R;28W3((-4AYS?U_i8C>2z$tO$<3gbZSnHfg@*fWa!QlU@|849u6hT zqZ=eiTRX@Y(zW0I9%4u1z56_e?{}BodOu*`Wbfwpk-ZQ|1@>={y$sBitj>o88Z`?_!0 zJ+IFF9_)AtnU1Y7Xh3Sv3Xx=dV=ayqCM(63U<<(|FMuw^+^Y3qLy!&5Dj3zMHf%P5 z(J1~B!QPwJFA4y-1A_mB@MS&{#^ZwJvc&sd3KAKVYD056Cc3eSPQXn}YyvWImflTl zgxMH#Lxch70HhD}EO~>H)YQFKIXC>Bqv34;$gVz(y-Q9C$*w*zrS0hRDvSzAE??5P z)=DMKPm0M3bS36icwLLSb?anHlXrZK4by4MdL1}Bi+u8h@|RzB*B`E58~||7k?{HF z!t>{QZ%}MYr08MNM;VmHz`Tu!Ha6!*h>cMH+4$ccq=OXEiP;suEw8nDjB}muf>Z8{!$7 z`Vr1Xm<=r*10&20og@HQ0znTnXA3ztkfeKzWa-s|R)x|ux7 zB#e1D6Dl>fdG~L`*pP>4#l;Pgt%z(}Cuvx-8`unkmdQG>7{-4^p!cIT`Bwq}egFb? zHG}oq?SW8~___p_yaMz~WnkC^*=p`XGHoduW!uPqT^zLef8$EaS-? z0CXU*PHbT}ix;Hh-c7uFwaSh&v67BGO=O#?bhd(2txF_Lh}EDCvELHH-tgnG9_04j zXZV*IAN~yh*!{!z9Sg5t-v&ajF7RbZE4%8dW4>k{Q!8X?Du*)7kG_L!=f-hL*(9msq*5OwJHn zVw)&hZW;c8{yW_Lee2%_fZYdp8xS^AProXt`UOQ(;EIyQl}Ove`LaZMCLtL;y3!=} z5GfH?qFNxXz|14!&CKI+ERc$a2OdC4JuIk*M66Rw(SUOS(-yN<%r|&VV6dWTu?CGT zRyOpsMMF#I1qOAX9t1|q_-izLxBl$_=++N`fbjZLM_mh>jb}Dr!dt-#$T&oajd4Ewsi~}Pd)JTgQpE1UlzpZ35BOn-&6Gzg|n!sN?4enh1(gw8!6r?I*Q?f z5|VP7cozvl8|NayMT!vdJ`&r+y;~QNeihmFB4w#emXXVzG9D*J&gmES?>^(-wtjU0 z=+*%c;Ls}^f}v>~v(`}`J3?@bgQFceVss3mqYWM%2&#^rI?N7KwC`C?49Tw~u%x)k zD2qtt74MaDqpUOOVt=k&mrCykM!&V&;n!#QZvAQjaJb$IhPoC)5Sqqu9))-=)U`uZ zP!-0(5wp>CoW6G7a?Wt;^gz5A>msRaIcN0yNo?W5Cpz7tNaWhTL8Bk8-vj^-*F9iJ z?;Rk3Tc7|4AsPJxpp8F%9aMVbdlDFmB0Zc#V*_^#4!<#@AFkgV0Pe0g0Al~^&k^_> zmdm%@1KwWKslj6o$Q2JC)Nj_{ch_$o0C(3NK-~JHfAB}$`(S?S2EV)hKY-HR`qX-W QQvd(}07*qoM6N<$f>i5V+5i9m diff --git a/launcher/resources/multimc/128x128/instances/flame.png b/launcher/resources/multimc/128x128/instances/flame.png deleted file mode 100644 index 6482975c494e02522f34a10894bb434f1ccc7194..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6226 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 YzkS2@~ diff --git a/launcher/resources/multimc/128x128/instances/ftb_glow.png b/launcher/resources/multimc/128x128/instances/ftb_glow.png deleted file mode 100644 index 86632b21ded4a18156a011115bdf671a8c998cce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12708 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�jIYC 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 NfP#!Fyha)k@_&Gtop%5L diff --git a/launcher/resources/multimc/128x128/instances/gear.png b/launcher/resources/multimc/128x128/instances/gear.png deleted file mode 100644 index 75c68a66f0dcc656fbd42028d313a469dd50dc85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18321 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

NaO6%(?ND*KfV(=Pzb7d7z8uVO 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 diff --git a/launcher/resources/multimc/128x128/instances/herobrine.png b/launcher/resources/multimc/128x128/instances/herobrine.png deleted file mode 100644 index 13f1494c4dd62b54e2e16942b23036e2968d640d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4937 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 O0000pdok@5If_ z3<2Ea_dWEG-LUV$2X8#t4o=f|U}xEP(r+rD_rGTa@Poj^_Z^`4_~SPMfBkmfef>=y zhR5$O?;m_nU|`=_MEH&%z>(4Z&_l+3_Zg2rZhYuNu>N)9sh5nG&lvXtm#)1!0d~&_ zz5&=5ItqEY59U1kT zn}Iu);r6{0mJDKIH~RQQ@YaKp2dbrMQ5(zF)ON=ZV2Y(ibgeANE#P5S9w*a$Z8^wH z*D@Ml*MWNvFRr+by~s@q55qX#a@zbk~O zzSR%#dbcAZ|NMuH`I&fev>E4f>U45YH(fVqqYYx4h87qY)+FU7234dGg-7Iu=YY#3 zS$|_JB#~V*Wy@p>w5Hs~s6|M==sYhpu7gigI5!I2m9I{Wlbe|rUlbQF3Lb$M-&O9n zxoMm}-GlrneEk9Ok@MpI`vo7xdiU~MO@wbN32@-%^U%?|dlFgCMKrsX_!GkaCc_kz{x>9LO0wyGhxdgs0YBiy)il?6Z*atg7pPzUzXR=Kx_`hWJ65PJExx#oA<0#2lRo8Zxs=~Z3u7! z_!sUA7oXa!=0oak*9UbShSBq=OFjwFCpp^7T9ZD$&dO>JlDij84)rI0({ z>XuHs=0Ij|&$;8WA0ooHj0E2j0vw3(*SoC@zM}KzbLwubZtC2P0_`}ZI5DwiV$I2G z=8T;@!Wo$722m+{Ma}{Dq5@J1NQMjC!6i$n5FpVyK9_uI@;xQ(hMf1jV=s6&OX?QZ zsUX)BSEDYSt$2G!Y^gAKBivxm9oP{kAnF>PJ*%wUk%+jA~ocJ8^yx&L9UY=qxQ zBK(dKpv2Jn?V*Q^2LT?0haZ0Rz|3kpf8N;G2)9F=t-V~gdkmT;4m-1PaMp~xZWh*a z_H{E&EHjbkk(_~09a6649&r@{WDh7?kpynI<1XRZNy<`g&3z$fYVLc=c~@x1Qr`3I z(~x{_p4u*Tnbb*L3Unc$HY$YxU~&htDR<_n3p8!WUANcGJ8BXvGu)op*l-?rzz=&A zzY|3GrW0Tv_=g@c9_#@>f8KZicy!%(Kk)cdHz&bkz&-bfr=Bv_Ubfp$-5M4fyVY`1 zHO+iB?69%5Y27TWTV?CGPI%V3d0unZ*wB#d14%XR74o2*BUy$kkW)YukHFo?4lZ}k z*<2m&OJ&U^-!tW1CG7_HUH5JpmOjs(Tb3ykvS?FOM+7340|}7aa!#1qlv!G8mm#nW zOD$*WTIN;9Xw;EX{#HnXZyEuP#O1-@VT1F=;~U}LX3bJHvQ!PsFI}}SknFYlf|ZPUMtNnsdTn&6OvXeJp8!t5NnU zxL1;g0=x%2_fo)(?B**1WPy?^lW=cMzEJj=vQN#mtDN`D)6}!iEqROQOxGDHN4YAb zsz?|>$+Og%v$T{KX3SET=fTt5k}V>>oaSZAAarba;`W`)#(C#~NBnn!1m6?_+!&rc zZ(P_2caP79_Uc~fMr$EA^Wb~Spjew?WeV4YW_Q4HlC6!!_BOUQmS4IWG#H1$&b;Y{ zJ_uc!m~1^|oiOE-$W9ts$9+v=tel5P19R1qA}LkK16f7y=Nyz2dLuC481AN=LGEOC zxf1S6%f68N49_zqO_lS^a$4ZI^Dbv3BjrjdS3#=4V-fz@kvhx1u&lW&PtBR;tf`r1 zw#>5-Sj;OH%y&CRbfoRf{PxWGN5sR3Z=$F0mJ{HJpU!>8qnqL0lXryKc8=|u$29kt zhwE|N=IB)ligoa13WV0Qj6DhFm^Z?+tZy?<8ZTP&dJyuoZDMGLDTRq)>z3M6;3u(g z!d>g8JdyjzvJJp%Wv}pD^@d*{dr+|f4CO+I>{g_L8*)55xXV0ecXgJ$MOrB3g(=UK zwIJoCdrsD6m*;?cBzcXzDz*lgyOY&9zA*Qh(Dg`NN{cqLo9tV+IIxGV-|xkTARotAY@HZ;o<%Q}HLndK)faRPiz^08$f zA~jn-`d9wU{umM#f4rA9f_!W*b7Op%l#IR=zXDVtT5ia#dN;lboa&Ll@6 zTOw4k(jaHX24kdAa1AjHBsD>*B~_AFZwCDSnu<&`sko5t4fsH=ggoPNTu2HgMUyIu z9S~eHlN?tzY3Z(oxlbgYm^3oiNS=q$)MO8}nODqGHHsGJVHQ|~mf^7D@yFqT2b@P9 zaX@BfZxspN5(3-+{^P{kx1Wl8X9iWvpdLhPmTekTHW;*M5KRLkH1f6)Rg`ii6d)RA zrkp^M1$Sk40-0Q3umo6$mb?in;JY+LK7`Onsu30P3Mafd@B#OT1dhxVg=$;_Ls^dZ zH#U=kWC5wdh&a&<6A~gz-kGzMG)5Xr8bKZz3_aT*;Em_pxT%(M9A?6zS_aYphC|rc zNIdWWZ#4lRcAjwm?P(N+BDyWsL+Q6GOQ*Ms9GgaYaEu9^MrAPzZp)bD8&biB-c32Evd6 zD1aia8e~YJa3H(8lF2){MkWo-eQ3^5uAzAz$kl{QljW6VMaXfe5Efx!bhQzq@%-~| zISJkr0bUXO$)Ij_8N^`2Hs(>N>k&DRA&kI>WFHDOxK$7$@`x-*W{^u%PUaLHn!z&3 zGq|jDcqJGV68ONNk*hMdaxYgg_}&co<-N&$@_>hcX9O~lg=fPDk_P4sB_E=qwoGb-n&8wfuiP!F%V7{KEb9&y!5EL>`R92n zN$`dV@Jiq>m777Pi7Ab`SdGlOF&@VR#zr0?8xrHda&9ClT$Mur4^j@R&NolOIo8kfTz>7@d6I3ryd#;4QL4I-Kn+qO_r_4;?ln>q zMCIA4AVqfqq~Hl7v_=A|0Sq_A#{H>;NecXwUisA+T@jTne96w z0d4^GChfTMT>V^uf+U;VcJ9QjKm7f7TT@pql~WKhM}iy-JP_aaZcEQ!8d?>HKDR~c z@{X`9$@`FW#=SDBR`&Aj?)wI;oDd+m+{mJq(S;V0L2Sfu6Yjr1@z`Smus4{R_PPnM zAKQQUKI5Lv16Pj&o!@1Rt*XZ6nhy+>O+Iw_Fc^)bN;%g-7H~zB!(Te{&+q>~ zH-Gf0EA6+@sv1xq{>&08W7H)E?ByMXa2ip=h_o``<4fee?~hg4Z1g>Iq=*NZ^Tiyk!=v zg>_96Z4k#{*d~q5ZDP&@*~CH*zH4O0K&X*a3$H>ZN*>8M{EhSf`v?B?sZadKQ|+xU zeE6yV;h$fbk4kve9e0P$tytd)U(MmC98HRNj%2S!ESo=e>*sHO;M9}1egFCkx7v%WD>@ zNfr|`ADM@dsaXAzYQrl;R4&%~-#GX8Kk%nded0$}-}B`1))zkfwZHmvSC->k4qfx6#r`Xt`}pkE?jLXc-sNZYUY_{c zwVi8^Ke?BeOJa!eZGZ6h+&a4J;s{|yJ;<*9!l#t>rb>Hrylst?NjtODZEx=>Wk>GH z*>QE|-r_!!`yOITo;OWwAm5O}zDV%I z;?@^G{FT4_FRpfzL`uQEw+pfiPhpK&^KkC7pQB&MP9b*Hm=S* zrB3c5dzmzHUn=_yX%}fruB$x>F3IdNnafuB)s(b_(k{a=C3Z{lxsJykTS zfQKIr++Rlj>}+c4Su+fiYQ#LOsj@Yaujjx7F)?Xu=0hno^4ypaaj#SiwD8mC{{9F5 z)QKCxU$rOZx4iWGAOB0gaIKpp?=yGSm{$+o`SaiXk#mpUe|6ei zd??&|`#f&9qk0kYpsw0c$Toqv4!#arC&UTziHQ4AF0*2!DSLb^;IG;@7Pq|o;V=JJ zAG@*~CGqmNp;P=PH=xFX)sj^vRRP}z zUn2bUnZNtNKXu~cKYHZ0uL9INhnH?Sdi>uN@Iej)BYPx;2sWgy0zqckp=lZ{1dI~k zBmwRwFE@}GW`V(-Z)+C6{d__x?X!yV&V&mRqLa}E(JIi&b6W+qDOiU>VmisRGv}%I zJ3N|S3!<-+01q47gU696nrT@Fsf*N#N>tP_(xwVoWk!@)kO)XYnElN&|Jw)u;Ghh!(S@ z#KK?!VNts;JLrfpvA&+!-p)LLcm#hnDe(GLz^Gqc8!MAVqg@6<4AFDMQ&bg#%S(0) zA(UA%3i#{4GvH6Jif6whp8dM>;&YIlgPderHSK@i+7?^8{mu5z6BgjM+u$Ad7;k@v z(G0Hpu6|XzyOBf1^VMBc7K&;&BVbg0pvVMPV#XMbyUJG?U?qfPF*3OGVqE_Avyx+l z1e6P~j7UhWU>T%^8w-@@SX|)V0xi`vvF4eL`vs33?x|z9Hxvrap)n8&OEWXWa-cR- zwlXCu?_>HoAnb3R{ktFhsr8Tlz9Tnzi@{Gz_{t;BlV1{7HgqH4huO6q+$ZFp1cq#CP zCxa9!g9w9Z*^#o91#c=z9d0Jf{>tf}eeX}LfBeUeUVKcu+5T4nUm3psZ=6RT5wqzb z;Nf_%x$d)Ff6R_O=7MKlhG#B`=5yi)zSsD!cNzJ)t}@lGXbs2dggf$a}Uf+HdD|X(*n1aYThwU znT`AX)ug~%k`bf={?VOYR^9#|+rR&bpBVqzb8oL-zN4IcOuO0sR{{Uo&x_A~%-P!1 zYX-Zn;NmG*9~t8TjE6AkU&n*;Gv*oQ-9ekR;?g!;+JWTICiwK{#n*lte%B8KCg&=W z4cM&TMr9Gha1-XB#iC4J6GJ+~R}I1lI6xejb@B+6?z76V*73m4BD@5i_@3;fpektZ(IeEF-lqPGo*3dMFa zu$1X`XQ<2Ml<;n)|AqE7O)vUoy{#AadoK(IRA*1e?&`GzDS!ro5n>I~T-b>Kdyegr z*~2tNVNR@<>5u-i zCzrRr@J7M^`XkOS{=>{-b__`UdkgUHTa2H4zwyDh_u#)0*!vg0HWI9iL(Dk69{Ang zUGZZd7*N0cK7vIm3sfOc5y;aSTDJJI!&}F5#*>%dR5FSC->Uv|*}r0dBxanx_WB6$aDP!gE6bBoO{0#E zN4G%6J>z5~FDq+$W#>g5TuGDk;ivxl|L@86))!w7_{#9>Kb!frpYy_}n}C1k8Tg6! z89)3^7!O`6pf>^kCf5j%S;_thMy8?}HRz7}X+j`cpt3+60`2Aw-gbD`;i)61TtIi< z2VOcPFm)X%R7lZWIJ+W1K-82Bs3E2?Vhyw&%+_ULO&-U%Mwks))HSp9$o6(P^lRP( z0rt1&u@gyQSb6Z|$;sxH&B-Ru5@nv#DZj`a^{a5Nc9ZVIPyW@PdvbBhORp9DPyF4? zmw!{of)21Ygg^LR9Xbsau(x{;w z5M#}#jVu;{_rE^?#chAX1o+5xpQlq6W%-n+cIl>^-E&5sNxc+sFHBgaa!GcjTT63q zy4NE1f8+nkJoAm4gMaG@_#@wA+;;MK(2f`RuW;-&NCE{8xqme;tlbawh!`Rv7&Vay zOfOv_%{p@1lG_A1qkI@#j*j}3=y$Fof@p5LurgbVDu5yd#0Y63v$bGW2oM;nvLV(s z%$tgn%gADJeGdSykpN|!JY+m_-WZL{|=A`+8ZqbsvM8DwWhzjUaIM8*YU#ouUC4;R^h{HiBRt^y>Brq5PW5fj4 znq(8CQBe`0Auem?>yha+@WAUNKrb&!77+It>+8n&a_BbKf;Xe!aS*Uj%#zeWa#=7p zx#L-QKA^NaQZJe-i;(Le|iMuC5rhk+u!JF?d7`+|&&`lK21Mp@p8KHZGY}qn&Zp z1*+PuO3SvwVlY>b%gjq6Cm9avDi(Afy72J(PKD{(EBg7D#V7trUO5m)fHB}l-)-D= z_88D^2HcVTuTSsaUttEh%lL8F`9DjZ2_u2C4OK%34+_iEsT*5Em(uK4U0j=>78&$ z4ea0bSPAe-3q6RS3_f_bao3qw0PgjU>eoR6SrI>4js9kxC(i$o;`UiA9zY0eJ@*33 z-6`$vjBYk3w+VG63b;S254Y=$!sG0L{&QSkGs-m4zFQcI6c}P*Okjj*42%Q=LaeFU zYF`apX9wIsfa6yD-2HT)`n@)-o4q_}t9BT2JxabD2ahB3ILe7pFdJB=!Br~|F$I*% zbg;|*n{z++-ZgvWG_GeqFJ5}?Isp3DcU>^vd+`kdZT*2aLxKRMer!#SL!LVKhv)`p zsjOK3wFQFU(l?%Bxi@7wozg8ka@V3&^w0Bwzc0Ve^5^FEKp!8d&Yd!}dK&dofCd;K zhLXlXj0g-N4{8g|z8Xl$c;JDcmeIc$oa5;Wf0!65P>B+nVB?u**}HO$`SvdD-h$LF z@h;=J7ykxj&9L<|(kr>Y>X#y+rJ}7?{k1y9s){%mguHB5ilYK*VFQC9gdu1M3?&;_ zNHvvJEK}r+0vj6v@YNcEBLpaqcS!7ycf(%XIauYpW<$?DGHjGAj?FbfX=E0M)}2egIG5|rJ&pt;gZBiu z*mr)S1ocx7zJS_FSJnoT{+vC*>;NCkPzboycs=GDZ~IfIK1uYI-Zt8H$+N%p81wC2 z<~w_|^BH;B;bnnnF#v-d=>EffaO^&Vu>S!4;56c?b<3BplsvTF76?!aA0UR94JC#K z1EtWQSm7bE90c<6h#jy`09$q2aVvi8(25_~7{VA!V=`m6I5hL2n++^`<9J`7fky5% zsKPz|g*>;O`CPec}M{W4`}}0Mx)r*DeH| zAqzMaxf=4^*$?x=*$)$CxEXctS%!1%~{in<#YG!uW@atdiJk5vq3BZCJbd{+#?C?z^b^ChXIJf$~=Mf_5jvkG?+IEu_85*NeaadIOyfjzw3SdhmFTK z!o}?z+i9qV*w>Tbl);Y#IDP1XBcuDM4YC4y^?pBK38ik7`lOH0 zus67!FWmD{6b49vpp`i;zW%x2V)x25cCKwP+u5a^FG;CGxnJ70AJn;y1RN9jI|TTC zuxNi@IX4Ujt6v;2BB)ZCCn&3oVE{gmSxwFrb&f1kr0c@r$)N)Y@Zf{Md1HJ&v}+zy zYgMP*RK~zk9+@-FVU?gcM_ko;7@+LU7IW-~%C(0Fn@7iN`yb}1+tK!OK zZUFjS7y8)#Yqi>k20s)E9S?6e`R?O@?{hckQYjvqa-hu(vv7tl-1|S%*5`Tyu4MBD zGoJbKS9tDgPq4jlmFeati|LfKT;i!iInx)PJRaPe?K?I=wE7HTJhZ+qdRxF^k#{Rq zV2!XEvzibpQjX+N;GA0@55UaqzzBT!VdH+n#rY`A&UzfqmUWPtHpO9WK1#;eLKs<2 zV^n}Qr6Ho0s6$!eUCOcd%d^#&P5HjzGdIr$5Y55~&C6ZGkSJB0B#ws3WHvEDrMO8WQNuN+icUjh1_TT9B;O-K9n`|k^I=9R%e ztlvGaqSGac-6hd=uvi+)F0gC^^RDJ5up;WoRzt3?-TlY^+oOOU z)v?Eoz{9Ouz>BNE&mnN%asCy+zJCA6inV{-)&sxNy*`C!pC5IPzOX+3$6vZ_2lx0V z-u2>%?>@KFoL}qel#Sd4a<17u{X;zazQ0DU)^U|goe)dep>{dvH-Gt8*m(X$Heb5L z_QqA_JG(4rOLFRP^@~2#8#60&e&yD!M)=A#6xVqLhx(2((D$>5`aX_zZF#x0{<75^ zK?EvHMf{srM2n`?<`@*ra8vCNYf4%fflYaPTe(kUV(m!Y_aQL}yuc zTQjTw>5qTq!rHJ7*Y5eVY`yc(;vwQ5z-(V+ck>y){x3hqb5A_QwU;lkd3l5B_BPAC z87URfzl!826zN<7J$=0kLLmay5A+o&)RVya4(eUe!}AxSj~585VhLbL`;tIb)}!DS z14JoQ;8C%PW57!*7V;~p8o0+Tz%nXuD!g$?&cPY-Id{2 z(jnwx^&bRU%>b^Zepeyjl|iy{J5~=3WtuatT3KU*^-6nnT8IZWz)Au@zXp?tSwxB@ zg5{;H`;pE%7&oJl<&8~Kb-{d@f(@%cASfszBH1g6Xsa)fO1<4T0Y1QNS2v9EJ5S#P zY!1%ZZx`%_(SI0x&#>{^b^3{)|A80CDay0a^e3;_!PRdtE+g$VLuM6QT~VbvkEXN4Y!P=L1xiUb8gG{+e9|B&J` zc6SZ1qg{8)E$Er4F%G8G1k8h6fmA>@D}|hJmDLZ{%dj^HK7!fqQSj?n^7d~4`eyCo zQQf}}zIsD%3P1RdKJ+9?A=QY z0pE8_AJ|n>!=Q$`E{C(D^;7@Z`g`uXuPG|q`mt*Vm;g_H{OL%h%Yv zdX?>~SJ~a(Wx6+`OKquKr?QA|-!L!Zky7Zc5Av@jfm{bhU~lAU^}QjWx`IIi;|A)c z+%6@UcgP)E6{(Ez5cr3BXr&H<0^TpztLWFUPmFH6B=KBN005?QV+68LE-+h}v9`~! zoJ#RT-h3Ut+4N9P8>|oQ=3pP|0cd~6kWQMyoA{Qs1Q&UV9=oP5St94sf($wSYpYcE3>7g_(0@PPaJ-K zpi4?^U4yFpJKA;5(g6XUdB(V_@4#hi76*{CVZnmCIaaJkFHDj!dkgCD&1O0R=xa~s z?spE4N`S+o-?QP2tyufFIoY=)vi z)xfZU$p~K`V|9d-%cyU=-UBE$goViAJ3t9D^~+9Hri~Jn)R!K!z1?l5TU%^zZZX~2Wj>p+T(qRLTK>@c=GHF^JJo-{U;Oib z;Z{5O-cUw@?{}iCaCybx*=(-g`1r5##ZQ06W;?s=Zf`T)-e$VH!+dv-#e6~Ar2@Xn z^Ohnmp_F>T_J5PC7w!AcwCaGK7^{7&tdFRYu+lVWJb<+x{Izkp&IKW0<_Fg~XxY9Z zeEVYO(4_M9+sz5kw$LDQBIk?|DF3jzdiZSe*FXSX`S@f_Z0)R`msbz}UVm(1FgXM~ zL$1X?#HGofVHxgb?4fG2xg@Z-|4{wc+iW(|mp=3BeC}U=(zdT{GTqr_Z+nNm-CdS@ zdn{%%+O8$FiPY!x?I(8bOCdXWuEWoGlaKqtA%q^EGWr?TXflTN2|BR`qd}QSTK1yf z?MR=h>kfZ~VC9JP@}Kr4Y$p_(7hlKgz=H+`nbCL!P*mUO{4ObCX9u`6+$z zGr!JsdzabXF4LXTW_wc>vpK0<(6x!=3H52(2jGW8YHSt3>z|JUg4pVZ*r7h@RlRRPu9``(lNV)&MkLz18yR~|&L*N61ssKGK;B)c>oLon1qhc3ihrZT8UoV;cRYf_& zeo4Z`ugm{TfU7a9tY z^YIekHG+SG1h7(&VZAy;iv13^>s|v2k=^P?c|QD)B+j#n+Brs`fM5Qw12$fGhF|%G zkMrc?UtzH~Wx6+IKHX!!yJz$1jOAiMJ73auEuIom3Fo>Fde*}ABELMe?E`*2A$pO0 zs0>Wcf+8_uERnup1M3s)#5$ar5KgV(Yr_&oo-HsJ`&H5p9th=p(A!2|cWxyf#?H2}+~mjx6m_HOxoJo~=C4H&^zZdVE6_EqM&a(RQ#{L07q>KDIY)14_-ugx@D ziTUZ&meV<1*U~Lpa!w^}yI;EPy}DSf2RH)mA>faYbDtlF-mi?kRq)692wse6*pyOa zCni*9P7u$Yf>RTG(9lhnrK7T=luPOSRdNZGWRrtF@BdtbUQAd<$`GFA>cMbds<u-M-`M%2V& z=$)Jp&z@j#;S7T_CkZFkXa&q$QWD;EkWy(n$ZXXrVNfdxK))u_GFWmWnR&)6Tj-o1 zXSY`ISswuotAO2isp4rxKjU*>=BgQYww!h-wBSpEEy-G%a$~42|Ied%`tiT@ca&9; zQ-#z2A!CY2M)Gi5wLSdG_x|-iYR7<&U^FVF*j{|G^x?cc9{jQzEaBU!s2{R`sG#C=rCxkO68Js)I`uQ`goj!#P8g`d0 zNg{V$2>`VTvSX|Dg;;M4T3<$vmFUkv!brFhSVER@oZun^QB~rReoPe@9uylJdiuh| z<8<3YwUgswFU3l&*S2u6Q1*q$3uPAOzBE@Wsl%1}zenvQ!b*OYiTIZmlkWM&sqN#y zU$uAK8}>VVw(@Ka>3a*dzaCH3kjvlw*XY*nVLv5PhUZAvCO-4Yf5oRC`6V{5ZgAzw z6|P*q!nLbc*u1vI?#?!|>5O)huFz=!nxB7Z#~D!^Jh4J@&xC{L)Pnx z+MTd$u~~~P+9Cy#?~i`popZ3rpsc>66p~CpQQM}DV4Zm=5v;b#47Q!Y;1~it^boxN zv@_fAdfRjK*j2GwX0;1XX(luc^1Uebgr?@qB%hnKFno!7t4!xol6zhihXCnUH}AQ0 zGw=~KL%8F%(t($D`=ZK+M!zhS#Vyr{Yj^x7wCX)kE1riszpG2A5yD%9YDk z%f!w0HoMajyKmb}&Q5kOmDvk_dcVF0K7>B;;^4b&C#qfo#2|*leR&-EXA}=UDENrD@4R1Xa;|NWmaVsO*)4O^$;+M4Q+jz zt7kt%>xnjJmRWS3dE#r2@r|!M#@4l~Y+c*n(o5Ia+S*|@Td-Ut+BT88O!8hcu@dnm z+eiH(j{}_@4%$8;Zn7@z1{HAKwjxjr1^nRv)+TV~B=N!-h8NCo`utgLJ#m5ygCUa` zi0-AZ)NEczuxyJB;NE8%fp^`30jR!|tXTm*ummj)T2pNCnCN1ro;g>dSYI!qqh#*> z|J|M0uVnXe-#_&`=XBq`eYd{r%y4E%%A^E~lC>gG;2@D>`@s%iI1kiY{)xQASvf-N zz;AiaOOOBo0_edDg2;g5fPxXgu}#~N&5(95dc!_iPx}!7#l@gLZC;PuZDFbRX z8Z)Y(%;q!x>id7j>DdX#Cnrp&XH4^)@wj3>Z)m*YHCh$5sPWbUdj6c2A00UpF@4x+ z{v+T&;-D^nK_11wJ4&33W`EF+Y2KX;-2N8Xr5!dd?XkbT#pTr@J6VsuIkfR;QDS+C z6=lo@2Hy^tBz{l2bgxD8AG;kJoYg?3xC*Sov=P&WtnuWj((5TNzueiT^CMtk1zx;q zO#hqR-XyGq+-F%+*MU-<%rgtQc|RlQ)J!Laj8UCnIz_|Gp}Am%nO0&}qZ&j_Rn7kE z!!Nw^egcqY@b#}mZM$<8KT{JRi3y*O^m(xJ^VF&|Ay9cwqk$jX{642=C!Cy}a5g?; zI-OxnNeF?uG3v(f-r6i`Tf?F&1hwIrOBP^>oIfsvb+Y8eKYA3r3-~4Q6KTQkVe13f zUdJ75lV09ucx9KJ-4Rc%uXE7LSQ9}^Y08RvRuE|+ z>MBtxpu(&MtLr53WN9ExwRHd=;{$XO%S*;LQ6ApZyGa!WCCx$zDx0e?GqY(Ln4n?e z)K4rhkzg}Z%`IplAygJZBSB+I4b|Uy@ciL}YB+!IoB;gv3&QS}a5RPES$qB?m`m|) zz}etgY7Nvuse*ELcEX!K{t;*6GbU$aW_eCg)l@!qf-2VX{{_EP&DQQ{5s7*B3-`|S zOPupBu8l(C@0_Rl-vj-Huh70uFKWK64Pmqi2iqi<_gJ~I%g+8DSGP8~+FxNeNl6u| zs%Ac$(@bXQYzFf(I)I@|0UN=4ZA+00?N_KlDxd^PBt9gun&3Uv1qbl)0$?$aJr{z( zNmvNMtf}idl$q&V9WyhXnue*7OiaVn)MlnS7u8ag3Nx<|gX|E`-v4;_ORv8d__WUd z-}^@VkiUPY{jzhYOPH-bL#2*Jm0CTO25#SZhiN`xI-NxD%UH4v?fZ4yiLQvYcuR?0 zy(SXt@g1-o&`D>y+vn}~FJ}Ih!@XtVlsjki_A^-NW9zH1vknJ4xXb%wSN7Suw9lpO zEv^k$xtwOK;m{D676tS1jOKKTPD?23m;maUORK8Yep?oRMzDGb_(Ft2NGdF8(5#_f z22L|=9l$P$^zI2Ej}?Mq)Ux>F#Luka*6D+i3 zxwiW;$iIK*>$lFDe)Tx;yT|SU{PeT%=0lj2(hg-zI9qv|+K1@;wa38S+ix+Q<>d1S zepP##5S_hXT}@!Ag>GTm+q*lzFCu{M+;^Q8FFk*kv$zC4oC{1M(9e*;N+fpdY`~>$ z+~qxbPwlgDX`lVQZLY7caV_h!;}Q}x=5@_L(uBxxHG9PTFP;6Z z_6u65P_&kyYD{ag##8hHy`J*QEAMjzyq6H{-PF6q$gj?PlWDK6y;iC7WoUfqLS1PS zY6;ek!m_aFyJ}aiqUH!l?i2ejcR&C3dx76QzWy~wzXz}0wuPx4XIH7r(^wRO@pR0| z$r1DUoOxMNw22)HbSrsUr;mk62bPjM=fE!F{sqv>PGAf8vSYXu7A`d3B{9R7CeUAj z;Q-rQgWVBa-o;%xAiKWL+EbUvP$qteVhx&$uW#I~_AWIinfRu)H(> z=gp<+{dvVN#{P}QnI6}&2ShyrkQ_<*7;D5`?0s_~7F1G$k1zI5mP`M+3}wY#k=oS*fbm+y$hnn#X< z3weG4+|ui{F@7&?D|;*0<_7F-!Id4{QwOBi4j4Xl!0y2|Pj7DWnSP(^Nyb*pAv0?l zP9{^%j!r3$$An3a6*bcM*jPez{%n!SI8O-=0^d`r;m203<~{>7B;xkG0y zi4%7Yu(%{9e>&l~klKw*>*y4^*o^I3jn5KT$s(I&eGMCJA_v>>)IRR&WzwfFv3Bhe zdzW^3W^0q@2SYxS_Su(&z6yo+9OrW$o}4l{JfS){LyI}qRIT<$3*hHPC(mugk6Plu z{sPmvnBznW zRoGlZwm0Emhvdp0*_9aX?e9j-e}1sab7`N;CJYe@^_-Rk_fO9_dT_+_@Ra6kisltk zx3dKdLO!Qu^t5NcG+?y}B`^ocfm}p$EINUx*7%-huo5`|FJILD4uFrqoxc`Sy zFse!{^quu_s87LVnD)f9Cn`OQGXqjnOXJ!&b={}D>8f$Rc`%!C@9>1yB*;wBnW*Gq%q7(d>({JQp3_3PeED7G|H%cQ z`@mKPLXB9!WfGFcNX0C}EHje~u}oAmGfOcNOc&a}nH1@Fzw*03^ToB}jSm8!Rs)~C z2EXq;&c*d=L4|ofqX~h~di+74Tj4Ge$uT%$3*Mrr)4jz~>@IDTU`v9qa2gg*Sd??S z^FB*sh_|)^8|$#Wf$VJIF71$B+GXYP4x78%9BhwxdVQVG^!j`z$#{x{&DQA5yyqw{ zxqEoZ{kso1dvHW~a)uTaG|fW$bvk>T_E!ppU^y_un1P!DGsKEodL=8Np<3~*61o-h zk1mXOasfD*ic*MaE!OKfye5fBiiI>lCaUe6n&<~a?Z;u}Hq$Eo?w5b}OFy%AwE02c zN9J{WBBFS!L3=nef;oz+j3M7*QF8}$$JOs9r_dI?7eBEi1akhS3qgoNu=tdgwcf>i zKD{o!AHwDkcD8W)Bi#Nr{mXj{_qW;F+2ZnOlc!gQJe&4;I?1?X!UjTOFbjdBqU83; zDYx(4^~i9zb3XOLP4JaKYG)ci92?pJ>M%Re*xVDRI^ z+T+~fjL0j&^(GJrqaL(ia02!W1L_t*c zYm4JRTGPLHG0q8{?Q1Q0`8FIx4&U|$>~7-rwn-0mSveT7vA@gSXp^Tl*0{E^!ZWVV zRhP0Sj-kfs4RZ|~R~5I$XS{v)0rzg*V{-q9`gDR-6;_8u**8pQX3?-|1fK?2*8Zs= z6GJ9AW(4vkp#-g|`<^ljJoCz9!GCfA05}~BqXTR{5t2l#Z5#znP>rqRSX><0n*PnS zNPq83zxCxW4v#-P_}YT1hB#%txyzB;Yn?ifG>tqIV#pVhJFT7Xw6%RbA$IPPIF=Pb zTIbI?#txk?aGvDps(AD=Y|w}G0c@_p&IsAtB-!7kcd*THZ^USC#HEc5uCETc*6VXM zNx4MAHjY(|7N*p|Y1MFhJmJlI54rW$9gg0)&-~$W)c&gJ*uGe3Ff(ocsDR+HIMcLs z`li4{k%^#FF=irC;3x@A!&>PHyPfm**k#fm%?Fs5LfR9mN)Q!dw{Q%O)tsTO?HiP> zq)Na0rQiPQ7l+4NPp0{_&6J8*VAR~mcds9}a+#fj z+x6ovutpZ0J_`$yxmcy@w0>g7h&Cm zej9C+s+{j|;-$~68iwg?56yuDUu=qPQ$}vzp*TDxVZblAFz{aYaiDQg0 z5py$wo0EnLztS+#z%%c!{U1#LMw`ZiQ|PBg?M+hDv~3hY;-)Zb#a_)Y4C#jQ8q=IGX4io-D>pQCNeF9=$e#8nwCX8RbH*M6W{%8M36M)Zu-njYSk?m(p%G^Xyyr_tT zpdp|xU={>|nW~stHz4WPU;FA^3+Dd$Xa3^Z4+Vbf2hjQf34yOvf6VXoe-UvGx4yn` z0Hv$u(WYsg5$7be8NgOHt336SR{su;#Dn(p{*sbUw`e(cP*&<7eDp=r#}e% z<%csv2<&mlmF5lJPM$?b=&cPY@_bRswM1e@s3QVcn+b9t)^?v*?9pFD3rS4nTEB;^ z4X}+hY_tJ;o5=n)$=)`D{Vg{4wz;&q!PA2w&t!d`Nir@=!pMYy8L36zqQJ;aIc;hl z&ga}dIpghn4|wODJ3M&nHj~>A$d675<5>h>+dc8YiahM!_|cc{nMeNR^Z)1ihXEfmUO8H?&g!0i<5DsBdie^k53V8N z*xes;df%OwVO@eb1($+`XbHL(ZF?wy1=H6OhxJle9l+WE8x3K!1$*1b{uasph_&4v zwstnSvbN5%s{=lhu5jHY9FVeZaaD&I%uOj(8LQ{GEV)0+xqEWLo!j@hck3R9@7!f} z?~v+vOqdmD)v(we2KZTSj|X3Ji{aiG!l@W1>f}V!P6eBIbu*NLOe@x_hH7(>_4_c8 zK7jxTyl}%fy=xh0-DIH(l~2qX5vmZZ6q5pMPOyQgmI4{3nL#3^No*QXe&a_kJTMLN zub%&_Ywr&}{ORFF`Hz18OAlvFD!=;7$?%_k{rj7P>>nl*x5oD65%yPa6NfXoSjAmb z`dquFC3^SfjIL8RX7pl9whUIW&1mv%cN6wvEn6`sv{TuU-` z)EHRw(55J*8B^adZfXwa1rN?9+&etx!QBTO-MY`@?gQozkEze5*t|lknr=F#sN$zH zvm^-@!KVek)qLet;6#NJQBFlT6SE0P$O&nQuGZw<^UT%2>#u((?SC=>5a0%I?~2_8 z-5wqXH+4QSts9?}PIQi0u4XH!t_apMB@?yOR1;H8ELaj5LdL)Q;R}c6&Hd}oz53L9 zf`7DG{__9$^22GJAb5V~%~ARLceeen{@kB#*3bU^zOEgRWJ%ndleFYcVg*}8Tfd3?}yr9K$e4{uyuFyd%CSX^I+O)o8~H z^vZ=QUlK>KS-%%e{^lCCvjuxw*xnYlH-eo_(yeudYpd)J23&2m{(yuH92uZKvXZAw z#bjP^Hp>~GO&A}aF*!V8djFXD!z1eBGyFJ5^Aan|wzDTjAzGGbQ>%%46#PPv+{`8j zr@)aQ4;6bT!UI$vipjATPO)rCR?g}9lCazG>Z^;+(GR2j@B|jXEY6JM<_+WWUE|)B zkaFymruHEz5`5--ngz2ABoz&bwUaBYO$)bs1tVK1-ucZReD27C$xEOA!KI7ffA#*f za-hMX8Yf?=p%$n6yz|HZXa4NZ|Lj^mdP-}%o2Vpf zI~x)7y{P>*+CVnfVKb2GG5fKz=rcP zKWe@Z&AxGl$uYu_U`J~1NU&ouITgpa&cck+l_XT;x#!>@@bX(9+VT6y0?=9k`1TC| z<+vZtc51)T8&=j7F14O`KX(pX2(b_(YGT1eOvOya)SOum%%n|wikR}7H-GBrZl3wy zdgow1t%S%YjGY`7=d_rq8K$PD2qyRc=r5}4zw-0x`g7Op_|I<&Nz3o+!on`mpCm{x zYiIBbvC&4<{_S;aV;BcCWGSFnSrMeB!I(9HstF`zjVo((UZQzHn9g8Y5YAe_$1`j? zk0j2rg0ewEThs|{mOO-5**j0~KH~6^o6?^7b^;s;_E3>SL60Pn6A3nMG|ccx0aj6# z4VxFe{wLA?Cl&yp&7s~fzI{X4*#X{?39zn-G+{0p=c0-WBC1|67b}LU&a~y|XvY?B zX)#7>YWkmkeDxR&kx1GSNGy)(h!KP;q9!KdRMm=y$KmvcZ#3)AU(4Lh*W-d_*R^-G zala!DVpwTgvsPDNxDr9{r7EXiJGr+DAmX8DkKub38FrTpknQi zqjpP%i4x2#VB)cmNn{Z(m_uq7r;-OtFil%dVdSZ*ih(mVJNlzPZ?Y@9>EM|wG_Src z3)YSSrmbI)K$6B>LE18YgF@jE=1^6!7^KA5yl&zIuCm0>D`Xx8Al_RoFKVbtsQfwb zz5_nupwk=;K~2QYO}=@Mrakiuff(ltXPBG_a;#!UX6{JPi{NK6C$*A|vSGd+c=e;5 z`+p+=xF7^KZdm*yMsTHOI`^!H4DTBfL4pRHB&cs|@^OKlidrzU2D8Rg`xqH!5oF?? zAyG}tG-(~i_%uv?I0`Rs^ziiq)Gwu2A+s2J{c8}%-p|0aFhFH|Oc;2_* z8_W=iNgmZxadNC?$6)cyPh=VVoSqa&RWo1rTzx(8Iv?rW{~HUyk`Ta+8(a{A<6PEz zL%c5~1SRdt@*2#7wL~6mOsO1NV77u;Trg>LPNOrE1SK`o46KJ)53@)D#Bjl!I!Pq> zr1;U>K7aN~y7KImr1;@mu_Bm6U5hanao%p_At@tJMG>t6v?bVf9DDF_Aamo-1)&a+ zrd3A)kr2AU1Topv0bf>xqG*A4#yN-2BNoqll}d!UAu}+iR z>S1SMa;(CM;7(9E5%Fi@(&+H@k~z7RY<>LT!^c1fd5jQvhNsMYk9Ds`YJ^fPNnOxT z2UDpX6sF{ctYD#!k(mUwV|jr7TVS`Yx4;fQbUq(HrMGNzZ&|V zyPnj4@!EN*w_VR7K2H0@hebzFtP!+)@@{!xFvMCuexS|Qw>kUPQfO77vl7*?ID?T6 z_~q~}a`%brsUv;cB8>}^v7o0Ir|M#Q7iXv9bnF|Okm-zmQjk+pmCYvsK734!p!X7j zDLvLJ#rM$AXh?(e>a4O53KKVTs(nm)W+9VEACFu@!3DDnBNMX~)P`odZf3P%Dv@lG zpd^N+vF$8K^_Qg>~+nKkvwy`5Y@s(-68E zCa?fw`JC_QTMbxcL?VY+OW&4A<`9unHBQAPXQH9a?Yl{%HpQ|W?+O~LsLO`kUC-XD z`f-B~9~UKz_Yi{9Q>N>cZAf2z6*Q#km83RHi&UFF;xa=s9EoXIIDn`mqLN~vXKDk2 zt(jRVA$U;}i_t;chNYxt7CDGO`Mp;R)x->Oi6qkNi(BdAdYN?NV}bK7;1Cl$R(s5c zIEgC+vk+*VO|x^l@N&Xhc{lLU$!lA-B6F8HX0e=0OXE_`3_TO!4CPF46ETaqeV@%q z@)Fi6s;X&2z3zQJR`B6t6aaXP5R68iy0%FOx|L4VOXgLoTD|M3BQqaTATipIPb`l7 zQW6WAqV&vckcbud(3qNuwh;nxQDuBi7hzZ%Eoh7q2vjo()7erJz=hfQ^0ZOi7-cNx#+XihmQ3-@95j}7ZL_AT8V(MY zlRO_g`0z0c06azrUUzLF5=$CK-E$TiM~IUi&DDunlbUKz zY8j~b+NlMCieZTvnOn4+nTVO0SU~Y$8IhWvM8cr7%#e->*fj<4=pD{YyS2RnhAe5j zT+n#mJ{oBmyM|SU6u=zF!R44u1(_%|G2BGN#wgRM^*U=3ms42*X^FRrjk;kTJUcu3 zNrMlcfB?Xf5E#a{Ul9JXfX{wb8Qu!qUvpD3PjBX^*8~?F)UqyKAc$!Y%X%)?CS5U? zX{c+Q1y#c=Bo0eaQjJcav|WE*ltC9#nzce8OU)_mgdZX+v>W~Xcnc86u3iM6ZUn0> z4P~GxR3bhTza_srMBC*kb9 z*Y$}A04yboxMAG5p}hL4a99|hy{4R=2DTBVheEz5l%-7RK~)KLEo7yj{iLiMT(uadF;&wAFzIqr_|#Yz4Ev#hq9wu~R8&S%Og*pq&V@n2`3U1!x-x+GY$; zvA9Vd!y{4S4XME@usD;)C=ksBnIke&vl+-tuo;e-7&FvzNs_z?Qb4aHSVbLb)~klx z$9n!}UQxdD$!h)b^`ZFyANdUyeOSPa8}a$8ug3e~VZ48Kdj2z~r7*iLJbajtrYYX{ z2Cf=}z?%86sTM|{+YUD}wJm~fn%EjG8Z(*WQLPmROhJV;$+?kC5{rYDlQ}#G|cCo)z!e& ztIK4L|0m$XCujk>Yxy6sBn!6q@3)ayV{Wf2z{@X3DvxtUI;N-*>b~@PjksupRYe<= z(!_G7HbiU{W5tj@Xdfg4%W#sSmLcud)7UW(8&=(gqu_kSN;`QontT_>ubMU>5qP8) zr35P#ipT|Za}nl<7ASLZ%n4SAxS~lVHfwebn|>v>ZIwQ7`EqphS{m1*pV=pUec~42 zNw0;)c=1KY8*eyz4^ng`qoz--gPtTqv#@5!ngtsoGDL0F&=rgopzk22j)|@6);6s! zTLZ+BCGgvAVLXA73cJ{ zQ@OAT>(zwNu+qd4Ef;{_)4vUK|G4W@DFCn#0^!9M<$@5TX@(R%a6OP-Ph8LHriVz+ zL^DJ(g3C;_Gyf^l-kl|=q&Qq`UvDQ-c0&OqW`0B@nt#z&2+ZT8!K@Lp8gmV))nZmR zq%|5EOlx{sgZem?Yo+pWIKwo28i8MY&YyAt01&?UO~>J(BhMYfVMJ|P5$?{z zmBa_U)7rU!cY)N0ICZ=6)M+411N|~k^a5F?tW5*Qy*Q6=WyM&3#s0R>_z${%QUn0H zaIF)BvW)Wy%F?mjIKr+_RSxeRL+@zRp{qg&jumx;;0Wrl6``jsA$7Sbw0Kj)Ii(WA zRk6IArGYY0l0wOxlBY_VD*dr?n#Bp+7eT+E?eOUXzPmov0s!w40$5Jrz6czCQu3N1J~({eSppn>!a!fXe&Ph+ zao76@#rg9Wfx{)?c)zRNsIdezEP>2VFyN0~|6d5edtHwajK#M;#IHa0gZW7Z{L$t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H1AOJ~3K~#90?VZ1mWY?A7Kli>@RbAce8H&^ltu+M_0u_jX7X}l< z(a3h{aFWd&ICE||A{hCPaNs|H3v95N0ZHR%Hz5`dGy+zFAX@Yani07i*7VfRcdvtU z&b{ZouCDH$uKCriJ9zV6{pk92zUTLk8*pQRX zY=AH@Jl6Al@|hChnoEQD!d}_9-v4yM^_BQ{^f)H)n8=jCZ6c3|JfY!0&+pLi9Sutw zZqtxx`I43e4GS8+q$SaETc7it{*8Z)M?`Mxdpss`?B4%#0w3#V`;5Md$y6X;!}VeS zaF)ogTwe))R|)>F2^I`2%L~ul9{VS0Jwv=j-QhCyX5j6LkQPJ66{e0IL;Un z-?)CPBK~V4Ga^$W6Cz_J^oEAkof{$>BC(u9`L`x8*7sWhBPDz$lgP9r5{Z^XM`ELL z#m-X&!;-`?i8+ZCnLB!(lZ#5Y#_C{C0JtFWA1imy)Yu!V!Dj>?(a^c0A(nGg=@8sM zzq0$+DDgWI4P80gv35Wh)3GA4P#~O;ZW0Lg0Dub+-weGYB6FI;^-CHiG|XrjD|r$v zEe(l=M$bth&=DCE=+t&G5M(7vql7a6$X|4Hdfw2feCb%xamy1Xl#W1&M#`ME;tFsdD{T2|m(uaxPt_EdXc+ATSBgDFB)+-0unB(Gl2`BkHkM zK&6p_oqGPVoU`D#`Ah-emEfbM$d^zAnG~L z==*nw6o@1;iKq&oWkaH)V*_j$v1Y>tSQGSYU|1;_OeIVe2-h40&*=r6LwvLJJ@HpG zL>hh1mL)A?)%>_4Gooe0MnTZ&^C=O5sE6<~K-8FHAOQ)1gb+R`@mtm^Rn|ZQtU+IE zNz~`bYp&=3nCf>t1io}00$dyK;5j6~1&IF>we%MneVfwoEiDroJh#J$5hFJ0!w1H6 zjA(p%#BGJ50q;;%X17c5*y4a1t5@B z0$A1|D*!SbDEH?=;1!V-C;%eMa&3UHLkz_Y?ez!2B>?ao#P4W1piwa{;vMC9BYtFp z0U$CV>FuCEh^h)=i4K|?km#bU8UYA&G;ByJ`V(tnA+iuYu`B?P5Rg3HDiDGKp<%;< zj%R8dUPBOE5CE4X{#wJj2Q-{8;y}Z?YsTuxjj;?s;((xp7cfl7dYBZD@UKV^G5rJp z$=Lwn3Q8s*764T44=f2R!2uw%B(fkoAS70dSP@vWV8apX>kNVm0N^=^pOs(-w5%92 zE;@dq;@&v^fXo3c6S7`sjTD5U4kS%v00=}fv8MupdH@E1T&(<6A*_I4L14i~uOka3 z{!$|o0)!Q7#;jPVmk`)oM-ZF?fGZL|s1A=#RLiLB~i!J4V6grKp4 zlj{M3?Evs;-Lrx`hs09`pC|r->ii+`BL{4lDgdTPCSO1x*8%{O0NL3A-V4a|Q`ueu z2nfuzs}i6BV6Kv2jsRdzVnM6t8w7&W0JsS8Ey6dn_D_`Shs3X9zY^YvkGl>|0)%1+ z8u6WDAcPV;z6=Q9wJ$2EdNd4-K9J^&drH)h<^wik~!=F0{ZsARhKt3>+1^$*>ZFo(kL+P^G7Ch>b9;KbHIps ziEp&5XgJbRv#-@S7G|kc$J@%wUV~L7z%J2$n_A2&T-z~G>)=2qbFdI{)EK>s( zUl4*zMB$o(pzlg~(QkTxO0D5Dz3+=Rd~CnQ^G$@Cem_)KuM)q$x&?nK2q+R@A2vXJ zEqU#Oo;tv(Bl~>Oea_63#!0Fdp$-V)RaG0eJMfH3<_SQR5P`}v`<4W+$S{mO-4A{4 zZ&mc0-e0;huCa2zg?LA5{KbLpKkG;?=tDV#Q!V%t_5grTj8rdGeBPli5xjvIokZ}l zi+xA}Yms}!eFq`u@&Q8OerwmVV9$}JbQ3MsfMGvC!vSj*let*=+bwKV6_`~Xm>l0b z`nS72;NSo7gPmXCx8HoT^YOnrneBXh_Vbg!!y2-1eBTCR{sq#<) zMsHZKNCeuD`vDckf%gcdW?yP`q^ifbAI7w*0?ci)*uJTN;KIH2Fs^^KOO#yw%xAfZ z=^RP0QRDS8lHkFsmjohWx9R?|>i(qSUjisC!5M$GjB8Q|D#@N2@=b#R6k|@lbXD2=V_{YWPh{VP~KC zNsYHgE$rl^0E1(DqUY7Fz_@4R!K(v-46d+Gp#Q&EH?ie2q&MCNeOkx;9g!C4&}r2c zFbU9UBW65E-mpr`?-$K7A2!jSgT4v2Ul1go z&n93PHfHaJp?vuVYW@Ehc%q_zj`ZWtiL^*xp7rTIqtP|Em6fhdP#JS;W79z_NdB$*qsnEi|28^?VKKzS&ER zR=eH~f??QrgtViy^d-Yu&l55yhy-Yruv0Q$12gEgpq&*q-qRAC-oxwvDg`7Z8ferT zX!O>GmKJN+v~n%q@l!$Z=C8?x)WJ{!NS4vM73$+-z4rj<$UJqgXT6Az*bV}}_9O)a z0AC>KnLjxFZ@Ka{v3LUs>DSF#oQX<jj497hTcm>3)EV(>y`2%?TDiFf0LX(t(Jl~}L1@d`)|2vj#HyDo2<+bh z(vC7g<`C3mdS%Sd%e(Gv^+og_349;;M!kZ$+5+}(qb^~@hBQPax(UEWzjpN+-u%_} zT*O_$qI$m<@bZ7-h;O}&!2!UATXlu_nfH+H1*qd~EphazzU~#JKwDfuTi>b#I8nRc zM7e)KRLvgLE08=v>tY4D#0=y*_I{c-b@k%}b`6!-flMVqE{E6#M({*b0$UG*MH0p& zeYPAek(o+~TV#F>+|_s6W)NQa%Y6d;#@+iAshnFP;hjV%0n;_snH!6zkR8!$LG|hG zoh|FUeExZU{#SqXuXaBE|Ni)R=i`6C* znZyEdqGiHSRRwD-ry7bB*yECq_>jOygGY`Kw`7HtP)XxNqt{7~+NXck_4Qts1FX@Vf!A=BffFh$OJ! zuepegUK_E4-tP&YZvX%!2U5kpae7qMkd#$S#M=NXbTV0|IoGVCZ8B1FJ4Ys`o+!M= zNIUBTLh%S}UBM3M8-afT{9ku_Nu9&9Q4lo1hTX)iz_XPf8N9u$=U#)-u;A1ks8EEViL_Uliu`RysgBe5O2Zo@y?n~-^>IOMBtyBPj65qzu z3K?Yxl}HSkVp0cLN))xDVMHB@*{HnuG4K%-WAKWs1(|WbPymUI*y4vB{D+lvL7PByL(OwJf|(ta!(BlTW^#31~^7C zQzLb24oLbHg!p#OgD6(U&+EMj5@7cd+du|Z2G;nIH&RL`yB#!G2h&<@fekh$f3C-Z zSd2k9Ed`##9qb!{?~(ZveF-p}!ymC}+yHTr9N{RRRpB zq`^p3Kp+5kptpXf=i37ibWQ?Dl*$G%cgR3SKbUc? z#J8c#H+%J~rTVp#!^7I{S_cBz*{IqAD;l@$jvwB&dmhmlAc!TPFemfA+Mu@C;so1m ztM0!uQo`FX#{N7$8Oa{ph}E0EtV}K!&X>dYSbAg73_`CeMhb@U00avfo!TZq7=nQF zL10zE;tKv6aRWOf2i;-&?$xZ7MYCj_`dXdCb>aBT3fCtv`}xVws8hNQs6!e?1lr?! z+}ZW}-T%ug{P?fOiq!JBI$J4eXVvL8yETnQTg;$dAw?sJ#JIpo=(GTiu{{)rahHC( z%N7T*hJrsK^N`FTneU1ekYG+Mp$QR28x=_C4jf5m@mh0;9T8tBYNcZmN3`_u^QRefCdCAE^F@NHlGN7q~Fhgn&o zC@^ND2gvY=y0vawHpQMI6J6?lO?3bT^lbvC3i|0_vjYyueCeb>tHcj#-PgVsna9sS zzu6Z~%K7~w(z@y6&8Qft;$I^AmG~y|gNBD=F|H-3X5n=!F0tgArJIb8S9;#oSn`0= zCFM>Pw5MXo4;81!$W*@-vUS>&u57IqocOn$y&7sWNN``c&gb*Q4?Q4U?`74}zF(pa z*pR&l*2Rs6PNS>>0Bh4p)J&}JHC*VstPt<*5n>aZ+g&88pzS^34w3^Hs}280GDj)_ zq(jJx8D#6VOn-Mz&-syl$#^(L{F|}rX!{n4kdBnlV<-}JQ}lfev*%K3maz$@S!*&; z8wwq!E6<%H(~D-nN7RyXUqv)X9JP99MaLKF#I2kZc&s1lfDP6yhyy`X^Uzwz8x{F7 znOi4j8W%XRKYh$ zKIaapS4A|aCA1+?VkwFgkQ&ydN&=Gt9i497ZksXGGVqN`g6wpCe`~i|G+O8L-V6YE z6;Q>$&!N~}ho1Anew|Dux(9*D(u^eSl1huIzS9KDE7tC!Vfr|GBd{+8WR~3_GA(3I z;1Jn|Q+(4vzX8OLUyqK+r;kCWfX?aj<6-@j(DwMJ?98{HxBkP2yJJz`yS=-C`JW!| z?pwIPG61w{)R_@zUX^84Yc;X=+U=LKe5)1XfB~Qu;7pMI0t@6>%lR;U+*1LDvY`|J zsRlYBX<2w+N~c@D%j|$y?9<2^+lZlx{cS+-U#s@jRb7|eeb=L!z_-u|2&{g>DyWQ* zwN)%&lSaI?57h%m$ZqNOH$G4_R{(4j1doZ_rsJ3}lme|35CroTPul`ZwFMTK6C3GnGZ>Pr-J%1LH*52*cQnT8o_OISL1fS$8VpgnL0?ptljHX zQ<#-C3f*|V`tyZl3DE77Ad;ru2S(5^|+f~t3*SIo>8GZKKP^5=>5R#c~ zN(51=IYtTst0!kYGe5{^{-4{Ivl2LP3=%zb|6VEUrsrc7yERa6~A*g?}LME#lUzEil+NF6Xj?h*t$? zfxtqI9ia?845|*+oDBlX7t8Lt-$AQIrL&)({EYq{LJ}jWGl*?oBwpBiRiNjCx1(SM|9zQD<`+25o;*fXjHaE`U)jB+ixs+g2V*w zk=s-6cu^1>B01;(L$K(R*N^ku%)h)lnNDC&2O zMdusoWw_@^<1;qNXQg^G5?CU$N&sdGf`LLjXFk9s zL15!g4Fp-w0SPNx9h(_^9a8sdwa0bm@5=c#U1|o8luk*i%10h-dwJ`xyDoDIh0qb( zq*K0EN6ScwZB1kiHp#7y_gm=sZG=H(7%18fxWxuIwXOgH)BV&;`@eKfxzzIQ%MrD0WnGJctALuzi zutscxBjxmf#lM87Guah8is+9$3DD>MRETdw75YG^`w$o~UYGp{uC^@OnjG?Zs6`ua(SDeA(vu>jvD)FR0XW4Gj*)?(7QaralaA2 zj-odptjc{J4Ap3Q)0a=C^#^e!)$ca9$*(c}Y2~+peP2eMUU^E)A$ev=RL3I5RsPz!4#P@V9A~u=&iB;=&XMkmQ?!=eB()< z=zha$kr0uUK3*k(50+T)C_5nZ3s~*CPDB^`#7ets&Jui_|HUo8F~x92r!a?WrT3c* zd;+{f=J()Rah_90UH}Bo2)qjl0(-a2x5zFT_HSIHen6*nM`8mA5CjB~6`dQZQ6VX#J4y?GmsiJnbXhRzt|Pi5SoNXY}i8d9n~ke!6cZU{qP z3e-J|YO9tHEBly2+xww=UmtiGFxWP8Fu30>BQ@D|Cb@i8tJ+NDGVsgR!+Lo|EBOK- zu-w5%WWLc4Vo|_3wyjj+8P@+MhG5HvH8MHN>gYWXjQEXq&KU@jvlE7qGcO5}0%F*% zWt}^cKda3dRhZTv^JUa5)wcY4SW&69>U$up6#xB?jxiI2~WS$|V;=kRCpL{_O zEC?k=U}Z|3inTeZNP`g_b9D)&XtKp3fvxKHsOQ9*uB4s>5W#VO!Zy0LPMi_GM#fL? z2t_)W3)$&)<^Exv;)}Ncq~;oI0EbN*6A3AOngRx^4r+>F?Nx%L^hcbsBlhGcUq~HD zjrwCK^YCTUgjj%(G(g;9V^lYaV*!EeRn(UVLPU`Q-cHEWF)X!FbySEv9NSW*g(P3< zoIhWo)GymgFF^ui2LJ;>!bZ;38NUDxu13me8L1Hd2DnGIUeNsoN_;KBHV}M* zw4i^BG>fmu98q`2Xo*bK4gedc-kbu0NdyCe_0l)09%4}mwddY7eDQaA6Zu>kS*iZ$VwPTW?am234l%ZYV-9{n4FRp|NZeQr?8(em* z^ZfkTFHRm|a|Ep?E3hEz5wz<%>SMTT%a7FA!=er(Q;C7cmF@g_gvUcqR~5G>jb(Y?xA=XD$&~_CB#k`={t_`-MQ86szqz3JDT^| z*<8o#7;%HnSjb9vKWicAyafY8$6BSJ5Z~;84Ki;2gz6gp4&nX>Y^e&~=Nf_l&Xxu< zGItdKV=Po7=FTo4Y#b0|e8mPUjann!iqK-4T*_l65zL(&6@!W*`uiP>^_Jfb#~)W) zHc!bo-3DwJ03r)g0m1?tDEzRE5oMh!*Xm?nv~F#0!(CCX4uGUXwX6?I zGWZL1oW%~1qV7cx3>_;*Dh*)GMv1>cCXKI19BXXFM*FVFS`1o_7n?4%xwTMA9wGn$ z2*^o9K~!f+1N(_>Uh|CS2ZGqTHq2HsiV>SzAfwhOE@Z7RO4LCNRp?8+t#;`3lR^6; zK2SuuiGAU6nGP&jl(`y%3w^ADfPpb++`m=;tVsMb;$_LMq`8QH-FxWeTe^0^g>cj*fEpPaJ(vzGn8Bmh8P8ps!ybr~SsA@CKlCx-_D1dfr3 z+Ni*sXevp}KLJ7wGYgj(;bk1IeIg6+7}wVfVGaHSYEQ#e*BlB*FUbl3Ii3+501^ut z7Iei9SoJ`#rtU-JxqnIK12T`uMeXYE{_-nk&I1C}WW_ch+*OHSK$wwvAE}&M6%Lh{ zf*6Q3wQHL)r4lI<6VEbdbze`brR`VZ)bVzue|PrtlmAW4C)C;j-~U*fLARB=`7wJ{ z0?58?0)k5XIct`T)CMr(2NnM^u~h)@Ol^Q$Y6#BBL+*!HSu^M7+V3(JL4aUwYC|9_ z$vn~P5lQD4NDlv)z#J*48qx8Tw)D}Pe;lyPS>^nw=T0R+t;GwDuvW?3QGFLb6Um+j zelLYu4ldoX~MV$Be{X<^E4p3i%lj;d6`s4+ir8!d`>iJ<`?V+US(}_A-Ba5Y|Y3j@+IJ5K(Nf?49Sgm zYT8+qp@d>YdYJyN^?*QAnJwEDstM#|-H%+0hszF6(K7vrx+pn;)L z5Y*dZ_{9<~UaPyip`~L($CAW}ivA(-o$g1+#lF4(uyqk2fJ7H1%_-dHa}Dnyey|m# zn$|r~!FoWgxNQpu_X$}$m{T$0YLQ7TGBr=Z5=V7ff(?T*?iT`fF77XpKZ^#6wQu(& zHT>Fv#G9JTDw$(dlnCjV?tD$AaTH`T$|Xp!>Wj^(%4T+vsJf}H2~B=R5b)e8d%J~f8F#FLGm{X zHXN$XAJZ{a<8P?<#qc{N`pXaX#sR?AHXuOXPPlUh2yDZJPK~#T64LAe1Ar8k*v_A0 zQrYpdW)vmIAYD49D9i?^9npRRY1>-IwvdyNyEb$?I%4Mm$B$XD(!Vp#Ka2P~^nUMW zd}9G%>nw=?@G-XCdf=fNh8_fmG;ACQ7Mdcq3Eg7_gK>SG>TNj%-)Ar!j#-Ca_=;X{ z2==9bxrFON_;v3w0gLJUixU5o=)Z0N@M{=c`i!s@1W#y~5jj>>a6rQWEzew`$c8Z^ z1;mCit$WP&_ObB`mV+?vHwlobBVX%wd$#FDr#bv}IoF6@CHO@B`~%jsY*^Cmi}-Ne z08p0O8pVU)3!>X&M`rBLX_%@cFqJSY8S78tsHNix-&rKxxry47=iA|Iy+jB9KO7TX}q?8qXT2jXj$?SF9X2YMbs%Z)HUXl3lCb)smyS7b0 z9k#E%jZ8hW?mlZ|wZ!leNjO;&sbRy4mI*R`#&QMLo8NIdO1tDS0p}MX9)m(DPUn4KL!^h z_GV4gCrFj@f`+l)FIlw?+s=Khrr*X9KO#-+L-8*&5yXkJxd@QTRPPD>8veQubH@pi z#e1f@-ZFcK>LBic_;B3;0R4W2p(+4T2UenfjHI4r(wTG`9+H?7X_eniIQ|W`>tW!j0>FS^^+9t2Zy|}>BRvn-6xq6EO5Fm+x^;Zh zh7n!%WD|mWUH=JI&zH+?uRg-}9K?s0)86=ctxuI}?kgAFQ?8nmd7?UZfuvLCB)%ju z(;T~06quhb?OJw@tU01?nVa?Y%suafI_>NoJx=JDAw3Sa=(t74Z9V?MaXkZlzX#&O zO^^Wa{6!`|MkWXRL8Zgj$gst42;4Gk)Jt=-;Hn7oHf?N}mv zHm=D068O;;aW9{Hu<;YmO?A8Fi;f8$Pqgah zsd8`2h9`ReRL^$*Q+@6S`kVRhuemZzZVOEv5FT zQK2^fe1HEu&%O6a?vs1pbIyCt`}w@@=S1mfsZf%$kOKffsiun3BV5t{9Z(X&xm(V9 zh;V^;KUC9)5RyZwuo)?RkN%a<<& zo!nf!ZLB@)1l_$HGIwQI0004~p%nFBW$mI2+UJRkc1|p6p>2# zRZq1wzU|@;fBcv^JR~(T4ZihrUa0w+DC&}Z>R&Jn_qo{hldDbZ7Q=w+u=wqe53lH; zaIoSMatk*&z$uEnXuVuy(@iusRsaczlWzpsyBVj`7q{oi2VV(t!#mr*$y@4apM$Q# za|Hbe*Bo&;T(%O+i5&12MCLjGFhKGO4w!v;5d|!Eh2OBzZhWyxGj~l4{PJ7RK4{O? zK*;>tkSxc>pTsZF<)RLgU;dx+G zzUtJ}l&zhe(yyTNotA=}GOE|VfCRBrLCdys!kvkWix%>39VOilsi~n!-*}-%~#cDrwspk z6U_#byH$Fnj-z<}B(9GJW!Az>bO7sAajd|^W56C1Yn`g)^L%tM?j3Q4Mqz1!vdYXLSg?g5gN0R{qh&{GlEINeG|Y$`hy9RNZd^t-EPqPeCz=VMXTGU2ZLKz`0ydg z#QIIP1;t)s_26PJV@1r7TlX`E6=9G~JUG;W0ciRhy!rh$u|lqt&qu4asW0hVUq9$M zM`Mc8{nUBs!HyW!6zqW#Dn``}N{bY=Rk;Qf;xWiVeGQZeVB(bVnKe#XoQdMB-sEo3 zf2M)+a2HRCwMx0vT6%kXtz2BnD(l^foTLitb42=ZyoL5495@Xk3@UWyTB03;0RR(+ zdT$z&0whlpm5FO9t+#Mk(_A<0*89!-KK?T5= z7XW>i`m-{JkqcHp2rzoKR)DNssk~o|&0t6*1CD~)Ej5#R zK}d2^1MW^vUl(R(O73M2D<^S10qmn36?uK$q`yP0K;d`W{MT^p1i!3TY0|yf|973A zza3Joj4DIDute?Bjs*hSqcL2sQUM*|>zNuGDK(m48=Hc)^Poq(_l!&R?Z1ng>#MI8 zTtU%={nkNRo;RtL750bmBw*R4eaUhZqmTs`@Nr>pJE(mx(%Oibf+P+By(2T4Dba_c z9YdmT12L!IqQB;#>Z78rluS4i_01*@lGA?v-?&VK9kd=fWE!@#&CaX+H|*OzeP3l5kIJp+5hY^ zsBp}2BF(RhNvr~!VW@sLdVr>UL;h?lUF^EZq+$x{J3}8!*NM;+QGEjSX5$9nHQnQJ zSL(1lgHi?FdhgiRMeDjWH-DfL;E&>Sa&rEG^vA_roh!KOr^M7tAHa>#*?2}vDbJ<& zz=bMin+T;gsDBv9?N?J+^Y*rC5+q$@AU4GF?mdzWXustvkb`9qb8;L>c{9+9T|5t3 zEllQKI6E5>lCOTp-8e>#4AmnyaTznwC>~FAuIIaP;|5qk(4qy?un@#5X^W67Cq#x% z_4S4J4h}x4c0pCsWi+~Bm^(_g%BVGv0AK{EKKcH4IhZEm_n#5^ay@S31~tja*{=Cq zTQGZe5W8_I$FS%P@Nv35^W)U_Kesz=nJt1>=^smE>Zo(PyTdEiqF&f`_+7(&{`_p4 z8lCj3Ljc9)lp!j{_~c>x!aIJ$%7KFeFF|4|`diAk^1hucyjrFV-22s-@`&zDTc19tqXePd`8#lA-`}ZrOU)~KmnsqRm|D`Oi zmLO%`$rOfV7SSz`yMKHKKFnXZmgdLSAt0trtLrVyG?AjGm{Ig=YwKf1ZtiH|&-d>i z6%+jzen)a-%YY4oGJ1W!l#jI6PpU0i9{@MFwQ??|o71RoM`u#*ft;L50Hp-6nnmTn!}sqlgMv1V(!t2;M9f|}sWe~Urotow|M?|_)zSf_yz z#n4pt^V09{lK{fX@!|B;&&1sPX<1oW&Xm-CV0#hP8JtF!Xbj-FwX)B~?U~qkV=l8! z)lz?7^^r(?Zb*!^zDihif6PxSRzRWOm!ChGxX6;km6j9|L<(FVV}vhc zkr`lNMHI(#1@(Hp(i7woA<{e7v)uaZv?vTh6EK168*5c-rjP!umGx5~vFWr2hCs1) zmekQ+#_-x0GL>IG9U8&xl9@T<+>mHL3&Tf2=@(QAOqA*@RAw9z&&7quo2A(=n4|S#(zRpOS0`#aZM57`SAe!%;2iBxk+U zq@pPG-Mj0kFJ9L#N{RsPD<>bHr<2b2sL`Xir+^P94PU-6MYj!)7=v^l8$%5RAG81B zl_ez@r>Sk@=H)dr@I(|s`JK*^ur!Ri&vn?Rg8F8*rPMr@x2tNZ8=-pSec6WPJiv3V zuX8dw_mEFD>45$EsW0SZO=S!h`AQ2qrdJ$2$MX9h>%cp zNOsh;=4zvIFt1EpC>8FpP)6rtMzWOyaH&Cq(-u&i0%8hEdxU01X!i+ z*eKrtOOU`qJYyLhv=PX=Xa@K&P8~%6U*h-fg=gM(XZv`{E@>tO5TM@UV(nZ)>pozS zg;=l7Og50*^Ph~X-dA&n;%mp97g<=nG)M93i-RG_*Jm%RDREzgO}DKpi7<5I=uCes zhR0!i;Fj{l<_oG@_>f0;K)~YKaOQt+acj^}=Uw&7K_Zn<*NO$eQOM1!MUH4M;kz3m=gJz{U%y z57>OsC`^9fi#wg7@&xaqkATX!JFd{WE-M1CbRskuQq_Jyp2o~>-b%iTZO6q00DxG} z8MuRVhV)whum_aT>~rkx$P}^S$L3UVqmNrnkD>Sxz#~MA4<^ZpRIZQyYEOFw#ow(5 zK^-B0o|$!}q#kd0sXeWG%NgGr<_1H^j=YfPCIgp9Z60LXWUzK;R z5YesTEYV+dk@X$nb|PZ(8Ko`X)7ddG=P4Av74d4hep>kcz3`_t2xRygbP3kz@m`1Q*GaCCP5 zDEZZCfG*-ad1wbdupNnfiu?%E&BGoT^(2d^FMboyf>f~8R`YUo49tQ7XYJqRDWR46 z%J(bMp4U#a$$>U2dD448USR?6;>}axmow0D;q_g$w*RM6N9-YcoW35c^${*{TB&ZZj0;nF1EYBEI_ zr`j7a_Tp80ue~8opE?NDGCd+LV7Vc@+$`{IM`5#)kQ5!5-0E#k~n4D8A(p7EHB4y z(_)(!Yc>&pFt&PZ`=|v04dqh%*tTa#8m4$lLDR+jwgdZUZTY~cgZ+1tZl*_rABQs1 ziEJL5qwZiFNjk6?ak7%w<_9RY!4{bpH|~H{74%ZU5DsEtkj3VV2v%%?=}8Y8PhK?% zxTMh`8!YTG=rW0K$WN=&L?Q|Fgmb9&4m{jsyGW$yJ@#YLz0EV6nZRK-S<0L{IePD{ zD`o(T!C`#rre4Z+$SJ!9Wns{gp-;h)AS?GWwt;{Sb)ef!GGXumWVh9L@-StxjCcx}UlIuB|aclxp$xwi8k>XPxrxE$N6ba3^6&6I?q! zzjSW3sPbu^|JHhlo$`-PFUIiDB}$>yb8`2dL!_UeeFpjM{{fJQCPH|JjlB`p21?f5 zO%0-spBawe0t&m4uIragals5v2ML1^Z}gttUg}<5=YMDW9t#$u1%1GLdU65=p2ACcD_95L3Sv_tE`{YS z8(8fj#;jcir%AeZA3A+7w!atDkikT(xO)_Ku8)HiD(uRs^YRy_$s#|x6g~r@Ft!jT zARkM^eS|{gr95jqgLoW~p*{@bDBu0tb*ZR-l0~qLNRkOALk0Y^ZNm^aOwm{6-4U0H zb@Qw_3b^JidB21BE@88osu{uLSd0lv^3b~vPM!jAW9nOf0QNc?mAS7pMK5PAwH={4*oO81OP=1ZPn@Byhn- ze>N3Q>EMW=sqaz|$dkj7TV4VI-p@trKfr5--w z1b)Pv-Wf`0^w?F*K;9!6@JFQ0BWyzzILD;l+akf15Q4LYEdCiQfC0I4ZIm3fIAZc; zom~;!4sjA`Vl-e(LUiM?{pV3#FaBhxT$7)7blho};rhD#o7F@;bzQkLY8uEnaYtLa zwuHtphV>HPe;3c)Cn6R9hj(rE`*9YicUS}2F2gM!d+MiQF4Hbyz7ml)m&6@jrN-g? zIml|pOq@J)k4=``x{v$ip2;45oM+ik%$5uh*YxjZn(%HcP7IX&_F1)!4uQ_W3VQg>&bd{6;up=oDc0x9To3 zZfjWiNm(7Ab~0LucV4We7pTaG;(rck861S57p;L;Tx-di;}#I%)YH?jc=eE_RHc*k+1!v0x}jWF(He{Gnfl{21U z?!k*Q13*7%NRZ>Cxk%l#~c2{Mzwbc6krjA7V6-b&&BGZB3KET1Ho+ zpM*Pw0{p!}X`Vra+_!>b*EIJdim(9kJdVHm8DMm1$bjevKM5T*&l_ehJgGt(w6XxP z<#*Fnd&1I&!2C;02Y0Q765Q6zC~Zv9;@G4M^lh}`f@cbDdW2S}6Lb{MP!60#AaU6&x031i zY*svH5;#)y?W=YLD;_BHPsY(I9MW(aESTm#w}DHG$pH5r0{lfOQ~%6USil#O$bOu_ z(6`Owu^5{k&)A@(Jof&LfByT}uBAT$yA_p4O&VKcgK`!u^mBpgg0ZEg|4vU-MYh}L zBQl^`Btq#;oXAZGh?<0`>E&L|tDML>htb_6prAGt$joL{N+ek%Ams{!vZjq(j(op{ zi6Kf^X`ISe^>wNIT&|OYky`KhNGb-8TlOrVV zrfU=JgN4%eqE%KB6~e@oWS9_JN7c8KW_Dk|j{GA zu#+H9tv0$phyDP%VI(WWYj6Xhnb;>%D^3jmy}R}CI`>!LF?CIHMw==s#oMK znZcWu^Q|#IDUM>W`BWCaW$^U7G`^?%?MfH$`9btLu z>k;|+z_UxaAM=7HI}#?kZ+shKg4*LJD)#UWfiNg|wENEl-C9wxE?Fo?Y3H-1k2tk%eQT(l-E~mPH>gr0K z@^vGuet|jC}skN7p2wu>D6JE`Y_nI?*lkKtJQpU^(1{C{q^KfXj_l+(1Rz4?b%| zajQH9BxkDR3%z31g32Gdr3<;%PQZlsrny<}oD!h;G!-VUNefpioBUX+&v)@I`1IYQY_SRaJ3(`Twnhf5 z7$C(dPjrV1?UItdbS#@Lk0e{-Qf!#+HfWAJGPuE7!SK1+yhp8e8S0M_M<6Fc0hRUj zzgtuuY>OMbpQ0NrrNt??&<&+}iwg{O(!s279`zCJ3^;15yHK;v-;@tB!EqrT| z&%(&8Cr0J1QExR07i&-)7L>I3+F>*l%O?LWzog-|51+@cry&DO&d=G!2>qQl$DOPJbYr>BX97UY?glO$@aZ(J3A zNcbE6ka#nMN+^%(_@t}NiEtq@rvCBbeWN?Q^q_*r57{h(c-+Wo2RQzntD zlBPxtl>jEY+OK~psn$|bd`5g_5O!2tR75pvQ-gQpWVvV%io_AlX!eIgbQhfzGC2@G zS{OZ89HdM8k2V>W0SJQ<)qo&SXyL4JfZz6%;0-&f(9?8gbO}(jE}DyZE_snXImyRX zBiH#2Cg89x!9uB*dho|A9>bQFZThPIA5+u^I`AlKI(y;7tH!VUdtD?*lF+&Q^$Q;^ z&S_80Q^eC6?-oNJ{3V@&5O|9#PpjGY`_-D}&IZI{GNNJhfx& zkJ+G@H%QYae~n%Cvjf(vENdIae03{{)MJ+_pJ}J3Haz|&@cf1^8f}bb039I}1gUkV zZdv-Hr$Kh~81iU*l+DYd`D4y%Z+__na>O%`Vgaus82zVtw{42^FijP%FWMJUWbIe@ z?@6wez?@VzRSJDT&FgI$9>|{QH@1S8)7RRJ3?>UxLPFUZ6+E+hzozk`re}qEtHu+@H zh8`@x0ZI63*%PVS_qL#LtfSP0>7Wmxn zrI(fK!n3^F|18&myPV>map|AQ1<=s9=Aaw1rR7}>E~V*sHIxx=uZas)sDeoagI1Pg zO^og%n#GG#yw~NK&l_tVUAnJ5JrE*c6t+f{q`7!^pR&g0TqvO_5(Ts85@dSF*pHQd zM0p>n;2TsN;1)r-C?I8|4EoJd3}ZvrGJO-t^G7?+Hl+UA7lpAZOo3~2h}A)FCVa*R z>dl+L@Ec$kl4->1thNz}u@%Apn1E`rqd|;)ZnECTebyC*eqHJ48|E6}JWo)}VywX2 zj_+-XM@>POXF9TAD+oYXK?#*;#+A$Cg?GXfaW!^d^x$gZx;Ug~`}8*b&#Kk?zPM=< zoBUan4AUQykk_5fE-p|R>S5*XyQG0LwF2JK_#4)at%*WHzdxw+x7rGxiWp9J`K=F@ zviL}gG?{V*W|ekoBjBI(+>}VWg^WVtdCYx|uMAwXL0oIE)!$o`SxZrf$jipfMv;j- z-#cl$D;^&BIPL*{Z>~MwbRpmgR|6e|mz}e-B3j$f!1n?eR3FGXCBzca;Hzc!>$`s{ zi)E2pF{(5W#SA;<pQIvy00Y8x6&vw>x zuaR$;Uu`JeEFeIZ#HI&0ZOu6S^;+EKq5sKzh3UysSrvKwbdVZ9@d6%pBFEPj-(k0x z(A+-T9bkI(wllTJc=W3yZ z!T+xsM8JtFnz$QJpUP0T8ykbt-s4{iiUnJ9oZ{K9Pp)a3e>(mA`>_}sO~@&!ql}p% z=Jt+n3(1R70bYNXlu)`{x^y*DQux5&1Uutg3=%%eka!o!1SBfWThE3h-;x#}U_i*8 zjgvKq0BT;JH3z@O-;rx4lp5S#S|WHJ=a_Kr%Rq8S7or|2K|FYGHrv**6F(9;TaHVD z%p`*22QR|Yng282&6#}If1V_Ecf6UIo2<>)vcY0pcImcE;eKa7dsv+S^(l*8&>4Hs zVL>pY8f5hZ^%-TGiOCljI&0gb{xoGwr*u;((X8c_h{L2Hut<0N+Tz4V^F1!H3UC}BiH5XH{0#ok^ef#Knu&Am3?WfB9oxeniAybg>` z{myfd5;K~WaYaF|7_$~xE)dCG=`+&Igje046$Ob+;$;rvS8(p)I)0Ef>hE-ArlyVU zH8l<`5YWky3VWyI+WKj0#P~zk%|m zMn;SE_4QsA@8<#Fk9hT$FN2wxnIsAsMNZ}QZ-GyrrnYBhmAvUYe${de3Jx zCO_3NsWyMMtC`IEEz5xKwjUQ=5Ls8ux9my%wBT4@-Bxes4IjdBKQyW$!kw%}vUk}s z%Z!HYoqrQ_s1ZL{U|)?~*41#iRp`V*qc zvtJhJEQT+_lt=xP^AUmp5`qZ32m^9L4c#a!SA|fPLivq+^DPe3Za)FD2VXt-{KNI< zY&n{xw!BbtVfs(m&abDi22c8U(ZSR}Gl17~ad zjNltc@p~}?f!t&gQ6;zfJ%ym5ObRR!?~P4OSzUtJT*%CfjT3J$3hoaMsuNPZi4~qI zl?lm6)|kh3>=*xaXG&!Fx@p-8h&5-OBP*keMvQK3rh5PN_I6rM#=`=GupkAa6Bu+Z zsbp!bJK(sAOH=W+D!4`&Sl69X3>0k6p~Hj9u6xu%gOU{Z?cPPJcMgdXoE=nMUmt$` zZ`+bn-mM>^4EVH zUV{lK2EEd}rJ`=HiG)VGa<%b%p=c*cgFn}c=L>s**U}580BNJ=s>KKbgvPIr;t91C z&p$IFfI|C%P>+sVG>%>F`g}X{zPyqD-`<89{1$Uv+?*$lt1vH+J->dq;66$E`YhJD ze^F-K_a>gXqqtJCd$vF&k}G8Iyd)JBoZ3(Eh3c-?VcyF#>xuWI{?Blv7h+|7W$)la+O;qczz=&q*L-Z0x2ch~Fq&?2F5yE| zM`gi)c`gVhChiS?Qs;Fnh!9kIO#wYf%xXf6^rRd0PK6L5I9qF%odWdNt`GOF+*F!; zV(;W(sP3${3|DSVhdxG;zdUOwF9M<%(PFLdw) zk$yHl3L<*=k1{lg4hB!WIy)mxDJ{MK6K9=6m7TH{kW7Ry3_U0XO)cccTprZZ&(;b& zeiQFL5023o8%g1w;Yn-ZY@+MW%>zRj$57~_^&7@j76lglFPNUy*#3SE!{2cEFg`I9 z8#MFL>bc6EaQk`R=D4A7?S!=I8c{q0{F6JZ5s3lwSJahHs#O=v-35WGLaY(|v z>I0iKx(*;)>wfukaCHus_v(W$u*tWlO`k2%pxfCX-!_4FhGE;EEaw|R_I-Qp7quyL zx7CC<*(_WrU%j7{)^DpfbAwl3T`)2*iby|*+9Uvq?W0J(fL;MTlgcO=3oKT6IesCa z?&xwaU|n{7X7q`mp2EY^q^msMbT8 z7p~f*$1fd@te~kW)8WE8^W&>DTqrjf<-xAx2JJC(2ytibPoX ziQK2hik@qS9WCuy0|smsW@e}YgGUu6x4izWP_R9_f~ux7U!PlE^ahgnTkR(K6ztJO zbBQ8r3D2M1zsrd#WujfrF+~0l?|bfCF|A}fj&Cv8-IRtB#*3Zq3pc@Bn*N%V@c5{b zQJguP3^dVcX5GOEufQUGmEtM9;S4=f60AT8Zy^gJ!^)<1jzzh$&ihFf_U_BK;O|yb%{xlyh|?lP`IS~;DiSjpW3;T}@!kM7S#zj2Z4fJ_CEcqF z?_5?}iMsh{2&jE;GO1q3GkoxkDxpSASQc=#{)Ncbtk93C5XW#Nb4)I^vESHwb*sPuU5jCI)QEM zP%rJ+qoH0g?`+JS0sAjtU#XH+^#aM7T%o^SYWs-qCPe<=sY5X^z*tsXA7s1g`oB`=r2mZb!SFYN#w>kdDCVz-qI5ZuQqrI8Y8LiCLw1ues}z;ui1 z?Tc^B_yOp3KOF9PtccdqYT!FSHJMAzrQXDsAaj%BStBBCuu%np0WU%YI4U3GU~f;5 zJv{-!oa;Z1i%*)kq;N&)`G)mAe3P`AKo#0C^m1B9J!;1M;T^~Z?HD>cT)1H`lLmiG zZ=p;Jq^Jb{rIy)J{@9xlJkufOG5UieUjpt5A4{E*@}d|9n3&V0zl*0Jphl>f<3QT} z8tXOO@#S-G@9{U*js&fXAK_DOdhEH0Xn0HiKg8SQxlBSdATz2_z{BELaZZ3@t@d@Z zUu(<9cYHZ3e5BnkgXE)^C6W1`_s)03HDmnZ)6cbNxR6P!TKRMRXc{~_oraFghYSx_ zuRja%>IE*r@UH+DicZXXq4g2B=J>$zrG&9%;Kbf>xGjM$a%Ii3x$Y$)zO;J0yEWL9*EywO4{eIG>e+4`@n6z*s)D*o)-QS#G7zt;eTA61>i z4OC;S6Ca~!j;TD8xWc&9mUyCT)eE213M7xorDbJdxzuc}twC-qDWh`df6{+QPLSTo4T_ z>+IE@AIf%)QB1kx$up8(CnTS)D{QLFn(`SL8Cy*o+$J%O`Ff=|_}GoUG+Kd!TBOwJ zbd+#yDM|p@w#>A}4g8aecJ=xJ9<%?d>lhx~~?}08i4OUE?=SF&qBq{jtfi z#?~*<*Z+nrL*wR!2gg|y3&Eqt_R?TET6KBX>?YRH_ zTJPeh1TVVdi$&d2zL1Z3Q3BFQ1^kB02f}Z)QG|ZyLwpr|{@KLJ9;c<1s<7X|cOURg zyCx8alfxLH=L2W^P*PrEcNt)Cluz5_O|YrdfZbwDRx@s)CFCkA^HOR^h45trlynxt@P^ITg|F+id?7BZk_^L6zzQ>^0Jl#FXdf(%yFRsSP z#ZO_d@$mUeZ6cTN_s+lm_n49*G@iOd+X^L0qo$dv7zc@8(d?T6ykK*dmXRVP-UJF9 zcYSf~m+%##`FAy2y7AxYyXG``Y_5D#j`>`Sd5~PfX5iYoysMNxFryt9x>}$zEq}RH z_Mqc*F^+eMbh#5ao|+OS#PAms&|N|vfRJBTs&;&EgXbQJamJ6i(8O}PLb@*H?$nna zL+vuM&9A5!aUm+W|244)9XZMU%ExWDJtuIpaB72!4bep~!}97ky9YtQ{EiYaP4>kz zR;wzqL^Jn!FK60 z<+DIe4!52<{^0$dWyw#y7=TYP{PnJX;6GE-fPK{?G7D+1mG1l6*;{nYuY`Yo^F+Ro zzI8F&y!XsCFwihD`ckh+vb&$3|Fd(PhTQNig~G;5{#Uq*i(<`4G9cb8cM4-Lj22gk<^4-?jjkqZUhfWxocO6ECZOBAb(fkFTKv-TY0K1QFb z5WL?dTn?1H&S7M+u?KKOl1QY+u6H7aLGE;`?Eb>_;W){va(+N0rHxL-D(0$84~n-L3G4_v-{= zfWv$q7GQu%G+{Y!Lq`p09PyhtgDgk~U3AMES?u2K1Nsd3j?J_#^A`FS+K$;we*P@^ zVmD`@3AfI-hy;7AF2{$^;QKL`)E>a;sdgq6D*()#LDomImkAVZwTw{W;v~rSQJ>yp zO?(h1Bprc(OI5DSX(#na&W_!PgSI+-%DtpcK>oZ}2<9UNWu+9~_IVRW(xps9DBs=5 z)O;NA{%$|jq9J=A#M6iJBB|5CtdcF|03YSpeSDd^cdpw;UNY%COW39H%Fb?8M?|i; zTY`AyaOQ~YYP@;x>i7KQ({_)E0j)xqmqz3JH_1sHs#eFGp{lw`g)#>!F=7KCZf7}4 zsd50If}M=G?#6{Jz06Lj$ZFwj!D8+#Lun@aMyM0 zSNrTW1v|l<+6bj{r|)%OU~x>bS`r*;B@dUmy~fRn3r7CekvX>k;2Z)H6Ox1p0BJJy)I;Y<$e8qp{lfDS|}pa?q(*g9l> z?&v#U9Ji{1uJFOH9enD)Oplm#eO@`sy*}v==I1;;eKz#z=BuEq@gfH}&_SfI=x32u zw29So?qd=w{^RyTtonBcN6Hqh6*sxQ&fsXI&*tYS$K>?;^;6rpmd*1Z{`-4!)Hu={ za{zdaCTLA32zu3x^D79N7n$*Fjo0&t3{!%vlmn>)1ih#NAM(-ZsTR$r@8YiaS>hap znQl7%@72se2NUdtRCY9G;?opmp?-4uqeA_0rY95J9_fS8CHlkaZ}1zO_HXN_%Ja<);0)qs zc$hDzsD%?ygn6XeDFwB~sPi4*N3*5x2Q4o+q)`=0pNOdKryY^y*#Fl7>uTT?6tK8O z`|aXFCfr)J-lRhHQG4L|KO@s`H8%ZRYBV5pP`eNzkpB4bgLZf{ckCX9;L$+enmN?30R%Xtl4 m+>lMn;l90ZxgoM~O&WaBaC?N^`a5B78la}6g{pjL75abkU$IsI diff --git a/launcher/resources/multimc/128x128/instances/skeleton.png b/launcher/resources/multimc/128x128/instances/skeleton.png deleted file mode 100644 index 55fcf5a991a78c85f1491aaac2069ba4891374f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3673 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 Y7@cK+bGdyu`Go*jnj%c9jC^kW526Yd#sB~S diff --git a/launcher/resources/multimc/32x32/instances/brick.png b/launcher/resources/multimc/32x32/instances/brick.png deleted file mode 100644 index c324fda06304c72b6ca19d95bbfc610b60d22a4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2388 zcmV-a39I&rP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1b>a`QF}{LfSL2qeH4j>G3nZ_wj+`I2SF{*rWR zMG{3(O8~n78P)#zyQ{zOBQjoD7or#MHGWpCP(mZ>^^fT`kqEsD5F;Qc5<4U|O;B_XP5m&>4afSohmRZd%vn@eDp4>Zkt?vPP z3i4^G*XUoZm*7*{{rnP3zhi{P+ZV>Y5B?1CZDPJGqURUE=5A~;)zd2Oz zWrWD+c*P2K(NHeT7|e|;Mu8C1H*anR_#B&lebW!53I^Q_bA*FQ=LM0Sd1#9#&x~;n z_mx6Mg}MO{BFybz43ut-_J*DD5CcĆoaD8X`KOBoCp=cvL+LV|cwfg+8$@l7yu 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 aw*LV9JeZus+tdjF0000 zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Yuvg@c4{AU%t1Slv9mctm)J9x|QWRNV$KECHh zJS!|AKs996fNKByx2u2fBZ^u%mf)lJ8b23Zq=Z4x>lf$UXgBZkM|%nHw|R9g5T-1i zcD+j4`xEB*^}(UTeWch)0#JK3yN(Gd<=3mHE&bP-Z)L! z|I(I+W-A-->N?>BC{@NTOVli;aVNViu;fUCZ{BzUd|aD;eTg50Dj4JmbAW?M=OrSudBv8UJX`Ez zJXZ=CC3On`M9|GJ##k_*y|HY((LyA~0LK7=5-ca{D1!mROch2F62y}d3N&=%8!>Y) zF?kx(s7)Y(tw%luFqA-+bBy>A7Z5{mX`#iII%2Kc?mBnhrH3ASI$7JSzFmL8n%S(;WNPe_ zlQqmLkwb(T<%9=kV2qpr<1!dPK-1t%2jd(K<_2dv5|V-mM)KfBRWld^g{~RmZcgrA z%>9Zt#o<@H(Vv)8gS!6*a|+bG^Y($YI{I9hjIAsjm}-mcYn=k(D5TD=kA{M`ko4O# ziDTo~7kFmIP#alm#U-8Q5WCbGP^@DUXOz47-ROMxCztsVc7#Jmb7_uhDNeQQmv~)6 zt83h?>lw^W3mDzo9nu6YFZ-lqeOQ=49ZD@b)AC{Vxft{Of@5>9xg`%+#?x?NJ4eX= z*g2ikuxgsoO6)oaFL7uDMUvS|YxMgrng3>rG8p{0gJ5y+3`Pay{R7#!5q|)hx zzl4RB*b)zLtAg9nwS`%T2Mg4ex`57=)-alicwT-WZ#w&%`RXt1|7x~++P&ctHVwJ6 zObz6AG>J2%Jp*IfthUi-wDjeFL||(~_+ol06l9IA!g)4C~n;#(T*f1z=@e17fo(ijka@PC`85@HlpSI!S%Cxy*|lZa}x!Pc*EKr}pav*B>3t1Kt9t^N>(%DGdmlT%kZ4wq3d8?Suy zIIN_@l@yP0^RfHn{d)6)1N%of-%7~|&`J3PsF{oEnT!`A#*aGiSdeD^|>ovCEOy)^RhFcYm4C> z$T(D}-A!=Dzl6mV{$E2R|084ld5RI=Bjg;0K74qm!bGl=#1-&?3fz<9@um z_qclp_!||bnqA|7s#!)lnGmwMRiWnT{x)g6H_UhmWs!ah~OU?$6Pu zlb$Dz5(~u+ zmOGf043&6_IHsr?<@>WPE1b7DtCbpS-IKpCl-E|4xlVHkNi1RsA_T~&p^OSF#A($? zF_EVIgol5?@r&e=$yEj;#{#NQAvu2VKlt6PS(uz~lR^og`^C0DhJl`4pjo%=?_=9; zo&bSo;7V)zs|{f0lk|F9iyZ;sZQ$a%t;u`9hg8%>k32;bRa{vGi!vFvd!vV){sAK>D00(qQO+^Rf2on!0Gw~st#sB~TmPtfG zR9M5+mpx9yKoEt;act~Zu`q}xeHsc{l#ZT~GeFHbxB?Qn3k3)ynn?5%kOpY5OiU~* z6!AlviPt}(@MN0ode^J{X5YMZtkci=iYnqO=ELF8YBW!VeDCr3g@}lH)N%CDxZ2w# zBBDd`*@S8I!TQHmP`c1Vl(7E&B4z!{!9MGs6u){h0P>6PS2iJVheSkl-gnoet8&tL zY6~s&*x6sZOSTTF%YckJ@>%W_t+Z0@<8Z*WBG&0X12{9N9pp!LR_r({B~O+yJA1^) z%h@^z%qQPdt^ttCXi7f$Bg{&xWct3YhM#nyBm+<_9L}tEUL7Nf+4G4wTeCmVmiT}q z-%zv2$SkKyjhK8wj}PEBYfEk#H<6=Sc06CX2L4M9QfL8pf}F1>1E|};bxL%L<2i(>u{zs2l=K}>@9J^_TJ9bZgNJwJ^-R|Snchme@Y;e)g!eceMC*Uv734Esj0000< KMNUMnLSTY38>03A diff --git a/launcher/resources/multimc/32x32/instances/dirt.png b/launcher/resources/multimc/32x32/instances/dirt.png deleted file mode 100644 index 9e19eb8fa79ce150c2d1820499c68294eae8155f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482 zcmV<80UiE{P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0003|NklcO`Q2WFLI7oLT7LA}tCG<{CmA0t zZ6g4dTAnJK^d+N%qR+ne2nqqn0qPc&@snCiZ%jpF1bch z@x22$0YeR3a=oD#W!7RTfX`JObswdj&LUUeK9k2R{sVxE9Xr6|7<0+1*k2VMAd`?i z05ULEC_Gzu;eI*J%cEh$uEefm^#ClgziOyI+2qRJgFnQs93Yzn8a^!hnB1&WP}2&4 YFZ+_B+4npg`Tzg`07*qoM6N<$f@&ejP5=M^ diff --git a/launcher/resources/multimc/32x32/instances/enderpearl.png b/launcher/resources/multimc/32x32/instances/enderpearl.png deleted file mode 100644 index a818eb8e671a46e99523f269234f60dd5c4ff32d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2120 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 diff --git a/launcher/resources/multimc/32x32/instances/ftb_logo.png b/launcher/resources/multimc/32x32/instances/ftb_logo.png deleted file mode 100644 index 20df717107b4aa636061772bb785c4116fa10c52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1607 zcmV-N2Dtf&P)_aP~Lf( zw%m85xumx7A02W@EVZ+vPiCh(;pV9fKvh*5M-_`Z?`>qZ^Kd=fY zKD&AI&z>I~v~OQ|HhwY0=`jqkp{GSF0ZO+2svOiPy+ne z@ezIDzT55y)pIACJ9@VO1prk=Wf2Q5sCp4$bZ)L4C;|rf@18ofUqXoa7{3BEH}tX4 zDwZ#|iHZ<4G{kcbr~)%m9j}hR_{`3ho_Ai80jR5LntnkKrW)0ILXzaO*lECztNM^6 z)wv|80j)p)WS#*tvyjV~sFKY#W`}C;`r`ikf$>uZ?mzJI_dfA0Qj7VZLeNFJvP9KH zgczd%+Q{*R&d%!-k@AU3N{vpsBChF2DWz!>-U&H=o-iwdxVz zEg7AY88{~*%K#)Hpx$#uK0j2-<$9uOQQ_SIo_lI~dhe9#Y}SY@15l0XN~%N+lcyiL z{d2%*=Z)L96x!E)8YnLe;<$5F^i4Dv$kOV&ZY2(h4oRy1RFN@RCa& z-dHSt{*E(e{zSUGmTbbPPDM!45i|jpz4^rMzev57BU`OzX6)(d$=}%df(D(@x>j;u zU$c*GtJVN$2&f2&h|6N~Q{Wz$nD~y0*xQ`Dvm+hn@)6XiQ&fdiST$e*#8;00au=zf zQo=ub`VECmq(1NAyxMc33eFK@Oh{cc1^DB&T4gMRr&_(g=`(F@H-B|<@=+ix)<8_0 zieQP7Q9TRj+#e4f%ZfL|<48U3vcd`J&d3E3mjN&`2V^l?7kO2@rzytw0Z%yRWD$UR z4GK*Paoz<6)KeyK84F}UW;2kDF)E-@)iedr$bp%eb52@f?AI81U3C;_24)vCU`eX% zWU;riLNqQI*=KOhEql2H)s(7ue%(LR+`N9cRN7-A@0l`tZa9QrkuEdu8kZLLp>KBO zg4>T{6K2s8c`3f~iw|((*JoF9vjKRZP}pf^w?()#LM5L9FFNO@60<+QX*RMe#$%d( ze*%~|4HRn*+4p)SAhzt8_J|Nr2^^e{BJ;Q;Rt;8 zqsxMnbWRmA&t9OoBXw1gRQ*0HT3T>yI?HzssGFHBWuTA+G=wGD!Id(Q%i{A{BMV0t zvql6+8e?@!$Gh|zwbJ?jv-k_;S_a_1{ao~aMi(8x-vOSYBEGbglzspJ002ovPDHLk FV1j`^2FL&a diff --git a/launcher/resources/multimc/32x32/instances/gear.png b/launcher/resources/multimc/32x32/instances/gear.png deleted file mode 100644 index da9ba2f9d51e05489a238664b90e88e4212f1c8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2414 zcmV-!36b`RP)V^V zpxn*>0>TR~WJ^6)OTnTod7~MOl|Zd^p|38S&Cm1rBtC4?;n=Qca%-y+8LsWAnNjYypL##~gJj#*>=71iUKw?umNsze1z<|Dhv=buh* zr_3z`1cb>KvfWXk17rG7U&4v_G>z4pkhZ;y8f_Q z`0B)clWJ|78Xx9jb;+k5KP5ktWZ4r@bM@uczC6bCsp??h>HdDlcOH7QIJ(PcMK|=G z9(Y&g*qb}4mTm^e!h^-BH4o;d)_l3^<$HdAVes6?x{eM8PQ}MI^VRX~Id#^8y;#b+ zOV#1wlqEkjDKH|Y@KBVE7fV3t`yG3CJz5;yg@wSYd?^2|KmF{Q6Vj8LWps;XcvQbM6Ykc zDMy}nOrL~@w+Q#IgD6X*h)@~?Lax1|tWYd1k+blDj+u_grk*rSv>s+!5r^}SCNf4i++7fO|B<=~%`t^LC4@)A%h@YWFWzyfetp{5Wm zsT%?%fkmY+jI)n*pIZ5gfA^-Ex{O%Up2?(tHt*Z^hKST67?#qpsN_t%E)v?USXE3d-QnTXMD$(HMpO|CI`X#TUzvFKX|*PDv5h|4KEB3OfgZ>#`=bQ6W`-+beW#*%7LtY$G&O>C zfp;#rxr2mIZeKQ z^S6w}*+iW8H+Sgk6Qs%ITvjLxpV@q9D5+_X^CtRC@9P81!)#qJp6f=e>J4Kx)OD#i zTSYY?m(vK2s>^I#2#lP;`*O$eAMRCSi!b)>ec%1rsllE;xE#YkTcav}+>=Hj`JBoZ zw~no)+Aw3B!htLB$UVVzvh$7U{MB@4^4Q9Q!`!Qms0UOo}?HxZ`i+S+8Fl_ar&Yd_t!Ml zlT1u3s;Z9w6jWY1mD(+BwLn#U>WlT1>>sbLKl4~|Y~#QHEM9`B$qR$4guy|s14d0N z<)Xqd1*bfFW`!}v*R0X|d9xThz3V`l{{d~LkTetW#F zs$S12%*J6-XOwH_!s`l%P2!RRo+ z5-|0bLcSVwJCZI<$YYF15~rccTpZOdFgjklU8Q#%^Z&Y@@7^82(Ga%4!as%6Ic3Wh g$G(03Zm;M60od$$bfhz$@c;k-07*qoM6N<$f&kONVE_OC diff --git a/launcher/resources/multimc/32x32/instances/gold.png b/launcher/resources/multimc/32x32/instances/gold.png deleted file mode 100644 index 593410fac9a7a929696dee968e112d24780a01da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2366 zcmV-E3BmS>P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Y;vNIPDd2KYw@i7d}K@E5{*v@m}NO#0e!-qFx`F|3j)@cz)Q&JIG8MQPV- zNZUT4ov#6h50f9;Rop!k_e8tnn9vT-!&|+BL_4i^eyUcc^(oE!%KH*}u}( z3e9#l-qpRq1w^SbtSnKpXvRO;jliy7!Y|_ASb?3f0d;4cLSb##NDz>RQr#|V|T9fscre}?$x*q(^!u>-*mFU#4#-fQf=t<@#x5mCPt zrPUFgF$RViA-8jw#}n{l)WduPPt-{lP+oP>8BZ}{2&{BvoT(<6ZrUu1DS+sxQ9wyl z$j-KC0|^y7cyi-0oq`ox!64Z}O@T9K>BM5DUzz@jiSS$vOvzv}gCQRu^;74cJgWCH zLS%G2V+C9^lnWSxx$zGx5Muh~jc0(5XVb55`hirzAkQ#IEHLT3rpRnAZQ04Qg&m_@ zTga$T_XG$Ld^5lp3kI||mW?+$5Cvlp#|VNFkdt+k!GK|=3XtTi^`rtt8on_mm}yH) zo<=ir6Nq5zv7aIsN+8QQhJJ#J&`@H_j+s3Nj*5kox88a7-UmODTnaY0z`=(QVvvxc zjV^NZF~k@pCLyuNfh=BvL=j1eRDz>~xf1LXS~6vuUFPg_$T3S!1@bAbaPcLSSfr#% z)mEg5>T9U6N=?nEhD_01HU$$v^58~QGZ+Jft{LKPPVO$| zzT!=B{E9dFjX5=_`#+ddpzf8oH>}lh&Xvj7+Jz&g+G6*0$BCj!lsmRhZ5nU*>gSSj zHI5AANEXEsz2jpj(9<|qe%_F zm~%D97S9C~-V3mj!W{0Krw12z=;~|it^5dCyPporFDS?fA0$1j;*v;2+TTk@V9zzw(Wfvq zIazYa*KD*|!BLy*)_iF}+Ky*3wS_%bh>c|gdv+z$5Q`-Q{ZwM$bmVCG+g zr6j#JE!?+bGJ>xygz!emx52l)jL21^7Kt;f9yq}~%T{~|xC6aa82{Rr?!d~VYx5?_tf8`0i!+%1>c*tbUGJ%!;Khx|Nr6jRmRx;+~A zw-;evk~&XC9QK7QnCpe#ZA3F7?Ukxx8?iTY9>L zu1sM**v?Y*$*K40kOmIbGZS4yuLTceK$LE$Q`%tyhH1-XR1cZx}JW_l-{SB$1^ z|6?Z6K=g7gIMVq&n_u#{A$*XhderAB&-0MTtBrBbI@;c5aP`aCczJ!j2?!NtJXU#X)qy zh}TwB3#5yO@APu#qmNg&+dTVzEX>4Tx0C=2zkv&MmKpe$iTg4)k4(%Z7kfFM0K~%(1t5Adrp;l;lcSTOizPKZb#xU7%UF?eAmTZk_;vXW&X}`>PFL=9Bb# zTZmKh8cJ}t~nO1*4f)jGB+;`-K00006VoOIv0Qdm^0RIx5dxHP~010qNS#tmY z4#NNd4#NS*Z>VGd000McNliruXvdG;^-41d8-@GHD|FaAIky+{v&QiL=lP*@MmY?(2;+u$MN+%`KI z_Pu%YcBA#vsevcr?+sgTCh=Z*GUR`6^AP}GfS$u1E4#P>0GPo!ONhSi*!p04*3;DeCQhK<&f) z*sGQP8K9n#+AZ>N;zwSUtoL@B#xqvOPE(T)s;gE5kvL&jI`#>j4(oySo k8KmZ2o@>8otPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0005mNklmU7q>t}A7K;7Jp?vWyZXE^2^aLhd6cr6?Nx}GzhbAiKn2vCjV^!6~Iu>Q3+s&RUP`s6tmP`fB*CGAac zm+sXWju{EhxqwDot=P$aI>VJjZPXd-y;mjyYmvPVn88RR!lv%jZ*V1*Nbe$7SkAbfkg?Z z?Yhm%*gE%n(KN^MdXaMh8$sI0%2c}wC%1Z*=Yb1>U$$?n!SS0c&j0`b07*qoM6N<$ Ef?KQ%XaE2J diff --git a/launcher/resources/multimc/32x32/instances/herobrine.png b/launcher/resources/multimc/32x32/instances/herobrine.png deleted file mode 100644 index e5460da31d7df8e6d20fa59fbd3b3b8f5ef655df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1034 zcmV+l1oiugP)Bl?e*rgyI8@@mp@8;MKnMh=jZi95rApdJZQ8`mFOoR6cV~7U zhuSkvY@Um)R5k(QVvFbr8fM-MnmH$M*Eb<)3 zu?1kGC}L_?AdX`JOaB}G6Cz03l&mfWsu^xI>I+!VszpvfMU_XJ zC7IMuX5bS>m@xq%0SrulfiaS0ElrceZ7|WTLoe5k*>_{sZc9nJ;5 z?1lW8!;Rf-pCJ8yHdpRA{P9<@pWoceG4u)yT|k!wAD(^FVXJ+Iuh(i0OQ{{+-1aqn zr2K%xf9`l|-h1h^!ykOUu!ryPRiGVG1sFg1m>Um{^AA9fBy{tU-na{U&g8DU+KAi_PYzX`oq_)(IK;7 z=mNSdSXsOV09ajG#OmCPo0q|;!{^U`0iEvkG`RQ^-hOM=jqg^M9G<@X8_Yp_pdFb` zv3b+`rE*6d-cR11+q%8z#;Z5y9qvaC97YRj+W&Q?OE(<;^2f^#zgl^!=gmI)^fQNF zKl8N1grKj0p;ut&0=g_%T&_B7t^W$@{3wlQrX7}n>ba)Y@Kz=VZbAYFD%5?}-ggj5 z0APUl-fxEhBEbYUbGPlUN`|)2J?@MAmupE1AOVmNu@NL~t9}&@#8qPqYL~7zf4+1B zmKc)^gm4FwhyD@am2cPgPcaw<0(Svku}s@a7~K+Zj`jAFi9DiLGAphClriyfX5{|; zp1XqtYcSRcG{@j&lb21`K~)6l!-A_o^1Fswl36y2!5+<+m@S0Z1;m;nQ@yixq}h2m zCE!I_#zwAL;&8+!(hl%&O7|KMp)8mrPieIpq`h($ diff --git a/launcher/resources/multimc/32x32/instances/infinity.png b/launcher/resources/multimc/32x32/instances/infinity.png deleted file mode 100644 index bd94a3dc2dd1a7a9a6d7c9f02e4fd9d33f793e24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1714 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 Ef}+7KVE_OC diff --git a/launcher/resources/multimc/32x32/instances/meat.png b/launcher/resources/multimc/32x32/instances/meat.png deleted file mode 100644 index 6694859db96b06391866d074da591ea12a9a352b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1514 zcmV(abm~6@`EG}0cjCXxL^^* zCMy~wC{01P1>N?DilyR@px_TMh+>iGHm2Lg1ww3tK)X_$tmCX>`_5!MGxy#@aqmQq zcf45#UTHMa=-fHa`#tA<-us3aqsb8h*pgThbOdeHZ+%fF+81pqtM%vp(N{*8EFc5~ zI~s;krBsw^Nx)Dk$R%xG!ANdeU*d&K-?5>t zyrOY25$S}?6HA&dn>NyMVnM@#rg_mcXgX0iGIXHjz?#gV6O0HK5^;(^o%xccMQw$7 zg%!ayrR%sVF$+pPJd@ZJJ=UVf<x25#+P&#G5XPof=606A3m&gr)E`qTzVKpTF4v*OsbpJO8YR+*qDW;=zaG>NpncOFR+m zBa<1PrI2{5h7}D(JyC|E|CvK{MYJffAUCgVMq3LFQ5d)T)V63%uFzLF5m>Gcbnh8pn#wwTEvoI$xv5I?wV)|n{tBGzGzBr)(b-ig2yV4Z;MhY0dGK{gR4q6 zG<20lg6i^pN3Mi#58sH$K?;jf0)WQLwfB!Z+il~SpA0}?YTFN{i|&VC^38tB?}Vo9D> z&J^g%9Xhe-P#;xdr9v(kWFPf<5?4i+G1qxuZPKoveEbK{dHcP&n!*~sn{D_0Bzh)z zqG`{xhC--)AV!;^w58&@sIt;RDKQjvC7xqduvD*@DB#?PE}92g-3>d;DvczPh~!LtnFA^*GS(Z!}@*jOB3{e0TkGqr#5nT Q7ytkO07*qoM6N<$g3;!_-v9sr diff --git a/launcher/resources/multimc/32x32/instances/netherstar.png b/launcher/resources/multimc/32x32/instances/netherstar.png deleted file mode 100644 index 43cb51139e7b412d5ca95bbde6d814bc96b695a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1942 zcmV;H2Wj|;P)@pYFrCXRUMgK5MTnlv2E5h`zo)0nppqtAID!NW1|7F)%RT z@%em#^73*WKuT#YE-q$zdV1`G5;+h7A%qwl94re4gV9o{^!-w)R4b(fn0Fj!Boc}I zyQZebItYma5f~U4C<}+fCtNPqPa000`h!>3bpXq<%<<9DPlXUSg%A_%?d|4%N$i_I zJRWzKN~Ic?%hl8P_S@HWUDt&W04Sx%=kwOs=;)t-8wVkw?F$`TQpzyU9X)a4_a2W& zAD^BkJ~l=wli^HV9q)E_dXLuDe!Ms{;{b)Jsj10WESCST2?!y?ojZ39xm>QZC+q94 z`F+0d)1@V*=jOK7{OiF3(%CFcjg3BcF!+&^PR|&Iu>`EJ$1UthKq)1Lhll-|rZxF} zzGDc%;>rrqsw%qAodaO_>#w;rG(=@>EoPxm5ptYrA%qTyy$HzN4&T?;Cjselxjag# z0MB=<3{;IH>LQ)Xk+Up>5FXPsH9$V!L%bH`6-6ip1_nHlNF?Am zj_!85U8ZSPYnpcXdo3*=3L(gvCIFiAHvo$A0|0J4evAN4c|3nD7K?w^b$zZ-D3o^7 ziJd7BiA2I14u|WNQXNXEYTLHmnx<6RvcCSqfZrcPDG&k$NFlHb1?*yxwM>Rk<{|(q zftwuh`x#wWV0>W#rΠ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 V|6)f+;rjpp002ovPDHLkV1iNGT7dun diff --git a/launcher/resources/multimc/32x32/instances/skeleton.png b/launcher/resources/multimc/32x32/instances/skeleton.png deleted file mode 100644 index 0c8d3505ab44d363cad65fff3c0eaa4a020c9851..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 696 zcmV;p0!RIcP)_>(Orfj25xK{%Pg!TrW2U?6bnAU zm^cQL8E)hw7-=BFuwXE3(0NZL2pWsn4PKzE4Rp`ccw2?ho*$b2tS+j4{!`U$p;8JE z5zFPW0XI~u)mkg16aoO2N+lB!zey?0ZQIp^h>9`hE2V~=PRGYut!6lmV>8Bn0)UN( zS4~1GwFUrJO8G07%emZjT{H~CM8wQ&Hv8^*9=oOi%d(^pLK%i(x~_|S>k0vYd7j5A zl?ruUtyaX5NQ8JYnGnzCbK-C~Ol+Dav6Pay*=#Z)1p8O&n=ZUV3futSZO~)awOA~O zlgT7;B9S20qaF+f#LMOKsDZ!}2;9J433O$%Sz^ZCcc&17Sl(*An}VzC(UkNbOo$43C; zJt+SC4E=tee(SO4?(Pou?{>RK4V)|i=NynRfTyR;Wf)K_7C)^&z!@a~fX!gqB%j%T z@9AhXBL4mILL7}oH}LQPyk9YZY&f{?iVX}N2Xg_=vv_Ku}<`KO$NnDzdk~M^qqM;NawtXn|mX zr#|yko@0T%@gC74!2;`B*NBS56j<9_%VP`N3~ymU+}DEPs9KgOHG%X4yeQWEuN&%ef%3L3y1%z&xjQ*VA6r3}2Yy_3mE3j&grxJ$}X zr@*?0Z&mOW93Hsk#9KAP5cS4e9=_q>i_T%dU%mK4O#$zeP+C?%R%DdbWzkg!U&isB z@8!wAdfjo`X#ig0Z-EU5UviFbxAW^a95?+X;L8e_ti)}+KQ8g~fa{f~2UZ=7KOC2& zJUl)u<5fY`^Hm3U2h;Ou0e@`cG2|c00V~PZWqMQ~0dG5T^$kZQxb209LEV>%P@n?d z_2SpBJCcK2UL;2W@BelUpj9B1xc0Mn4*a>!z*m6^aN-eW6alzd!QZ@?c{q0v1d76v zAog=$kc6UMt&kblcdnNOU%B&YiSOV4`A!+%{@PENM@X%V0t|TWFW)a<2%N|o=%FLvMMK(|;^!Ii4-eul*!cvlI- z5Ey5ZGol5~CJzdk(Rj3IItKmIKmQ;#Qtd9k}yt> zPA|`ae13}qYwf@lMFawHLBGZqyR>8+%wcT>cS|e}?jM#|KKg+=51XhO2c{3M^@NFto444TC3=pvIb zF+Z83ly`oii>!u;@E{dpHc=Zw2$P6%vn0;)z?-M4D(a?bNax1!d#6a^!Pykrb~&HT zvu_(5aSE1Yd~h@uJU0s!Q@~DmKBJ{2^q+Ynazu5Ou`I+DpoWNnSf2jj-EH zr~f)zSZU{U&XwoXEh3n?MI(?Ft+juG?Dr~ZF?MYesjWs$n}kHTI1T(SFHZ#({0DPf VE&;%r6@~x+002ovPDHLkV1iLx3Zei2 diff --git a/launcher/resources/multimc/32x32/instances/steve.png b/launcher/resources/multimc/32x32/instances/steve.png deleted file mode 100644 index 07c6acdeeb9ba480f86db105b6493f168e7722c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 969 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 Ef}{X{S^xk5 diff --git a/launcher/resources/multimc/32x32/instances/tnt.png b/launcher/resources/multimc/32x32/instances/tnt.png deleted file mode 100644 index e40d404d8f9c0e247f2579761f53954889a050a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@f>I@}5hcO-X(i=}MX3w{iJ5sNdVa1U3T4K6 zrh0}3249L60#&{Aba4#v@J`iG-NDVry$^H5};?7#duIbmUq!X0hjF;4_X==5NP8{IOXJ0&}p0?<&w%hp*Q&e zgFs}5!7{Iu$dt7L?#`(%Qy7aZly-|>?wwJ0NVVxvvtuHo-u-~H>>`mHWgl$n?E~CP#fTzrG=k;96XkO^H zIEb4s+ADK106k22l^fZH~tDnm{ Hr-UW|wFG@A diff --git a/launcher/resources/multimc/50x50/instances/enderman.png b/launcher/resources/multimc/50x50/instances/enderman.png deleted file mode 100644 index 9f3a72b3a093df63a45e6ca6a651bedb0943de8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2429 zcmbVOdo)!09-ro*u5-MH9yhg{sdOC7-Xp`zC1#9g5gJTfGB0Cf9%cq(iYOZCm>f~7 z;}PSQdl1Qu9^{Hr6sA&9UxGV>B zpp6&kC3I#-aNT7h_AZ$>l_}fHB(Ttq+W>nh5mvxsOXz@<7sVG7r4Hy%dWrCUQH(_c zpCFRG4(QKL(Y$;BXMu+f=>?U2#G{U#A4&(;;iDVtpp+t79tP`SP+NB;VfZE{r5LNBkciuvL%iGovxmC}V+$O^>rc#Cm; zLW?DS?Ekp&O|+OAFJxo=*kVD9hzZvt9J54*bN9!F7J;xg#GN88Toif~MZk>VvH21Y ziUS(Hv0`yqM9|s}gdp3kpe+u>+d&Wk0tt2$TZrTW5y;lg~*^f_J%Tf~iJvs^_29`Gr{MD8~{0B!zPCCAkxEp5eUsP4+@DY?NJ9^1>y<66*tIe~L+zC1X-fKxdTuj7`&l3!4eICi-260YPz+;>X6TSR^OmfdrXLRw1DUHq-vEL(A%T10Wkp4h_rs1JkPZY&}XZQ2La2-{u zp>1c!RHm+;(qA(r?4XhYppNOsp`30Ze9SH%9^**rM0eg^9KR!A3f3Eypsr_^JyhCc zP_FnJ(WD}ap`V9Q2b`U{7``ZfH_!C(xyvh)%NpIa4r&5NGh-f1PEKx5X{r;6>o~c? zQ&UwMX|Hx7%R6@{Of&Fle0>oXMPIP>YcC>+2OT?xB#TNvS-|-!$IDZDwZ1v!G|r zsBBDzI6p5N=(N=#J&+d)Luw3s&nw~|>TMFIsCA+@xrCovtqDbH@~*ZrdOqA#8e`74 zKa?wlBMmgNnNUIAKl)g6 z`({Q;I?|mPJ^AIBIrUq&+u_2;y&bP&Lhr2}cJGUAf0*HsFte|CboaRj3x(~=?G4UF z)pyAvow7`op&I>{3*P6{J%52bD>9xo_$lyr6WiMp-Sy0Xw$SCtqh`gbv6X@N_|%N5 zmoFct@N!Rwt;@&s+;XILU*zj~i)T8gJ6X8oWAl$=@@Ic4>W}dO?K_FiQ9&r)lZnF2 z-2sk8gOWXaRet1LPe(>&IOF?(8`M(FZ%z#PZ{ZHzk*OwB$D)*;C_h_fwtjkUshx6C zJsg55)4a^-=a{{f)%$7PqFHF%Is8$2AUfJt^tZGwix$+11h+w2TS1y}wYefiIg+)0 z>K90_#QTkfd`&dv$fF-1lm6Z0GBPTos76(EbFJ#JnO1s(%(G(|!!l$Ag{g4jug3KD z_U2T?y*rhZh$I%Lpx65XbwXytW`_FF)XQBl=z3$f?CA}rszPC~V#{b5rL?L`JDzUS zQ2sHP$jETMZ{HAn@<57-U$3g|qCOo0WIh4iWqDD9V{g^!@^|gYG`AQe;ZkBilA@Wf zJFD74LFs8!txYCNGMk=^F>;&DYX|5qcl(gz+?J7mR4|6_4eW27NE*t`ZglDRouxZ2 zF+&|XbM~zNh^^N3?YAzLK$S@3@uXD?`9|`}1-BM*^Sghp4eXu8C-C`MuZ^M>+V5W~ zZFK|G{msiOeojcdcQ^EA^_$t2<{U?RPI||E6Ze)w%glsF9bWDpI`DpSdAhw_^d!OB zERgIJR3QA2TKxL@hwD{Y4MLRcf~xJ~$B{SJ3mP<6A=(s3y>g;K&EgM{hl@9*f*f}6 E-vjLh{r~^~ diff --git a/launcher/resources/multimc/index.theme b/launcher/resources/multimc/index.theme index 070e23f1..1f72c0a7 100644 --- a/launcher/resources/multimc/index.theme +++ b/launcher/resources/multimc/index.theme @@ -2,7 +2,7 @@ Name=multimc Comment=Default Icons Inherits=default -Directories=8x8,16x16,22x22,24x24,32x32,32x32/instances,48x48,50x50/instances,64x64,128x128/instances,256x256,scalable,scalable/instances +Directories=8x8,16x16,22x22,24x24,32x32,48x48,64x64,256x256,scalable,scalable/instances [8x8] Size=8 diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 270dd009..391a974f 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -7,7 +7,7 @@ scalable/reddit-alien.svg - 128x128/instances/flame.png + scalable/instances/flame.svg scalable/launcher.svg @@ -256,58 +256,32 @@ - 32x32/instances/chicken.png - 128x128/instances/chicken.png + scalable/instances/chicken.svg + scalable/instances/creeper.svg + scalable/instances/enderpearl.svg + scalable/instances/ftb_logo.svg - 32x32/instances/creeper.png - 128x128/instances/creeper.png + scalable/instances/flame.svg + scalable/instances/gear.svg + scalable/instances/herobrine.svg + scalable/instances/magitech.svg + scalable/instances/meat.svg + scalable/instances/netherstar.svg + scalable/instances/skeleton.svg + scalable/instances/squarecreeper.svg + scalable/instances/steve.svg - 32x32/instances/enderpearl.png - 128x128/instances/enderpearl.png + scalable/instances/brick.svg + scalable/instances/diamond.svg + scalable/instances/dirt.svg + scalable/instances/gold.svg + scalable/instances/grass.svg + scalable/instances/iron.svg + scalable/instances/planks.svg + scalable/instances/stone.svg + scalable/instances/tnt.svg - 32x32/instances/ftb_glow.png - 128x128/instances/ftb_glow.png - - 32x32/instances/ftb_logo.png - 128x128/instances/ftb_logo.png - - 128x128/instances/flame.png - - 32x32/instances/gear.png - 128x128/instances/gear.png - - 32x32/instances/herobrine.png - 128x128/instances/herobrine.png - - 32x32/instances/magitech.png - 128x128/instances/magitech.png - - 32x32/instances/meat.png - 128x128/instances/meat.png - - 32x32/instances/netherstar.png - 128x128/instances/netherstar.png - - 32x32/instances/skeleton.png - 128x128/instances/skeleton.png - - 32x32/instances/squarecreeper.png - 128x128/instances/squarecreeper.png - - 32x32/instances/steve.png - 128x128/instances/steve.png - - 32x32/instances/brick.png - 32x32/instances/diamond.png - 32x32/instances/dirt.png - 32x32/instances/gold.png - 32x32/instances/grass.png - 32x32/instances/iron.png - 32x32/instances/planks.png - 32x32/instances/stone.png - 32x32/instances/tnt.png - - 50x50/instances/enderman.png + scalable/instances/enderman.svg scalable/instances/fox.svg scalable/instances/bee.svg diff --git a/launcher/resources/multimc/scalable/instances/bee.svg b/launcher/resources/multimc/scalable/instances/bee.svg index 49f216c8..a0870984 100644 --- a/launcher/resources/multimc/scalable/instances/bee.svg +++ b/launcher/resources/multimc/scalable/instances/bee.svg @@ -1,159 +1,100 @@ + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + viewBox="0 0 32 32" + id="svg168" + xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/brick.svg b/launcher/resources/multimc/scalable/instances/brick.svg new file mode 100644 index 00000000..1b5f65c0 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/brick.svg @@ -0,0 +1,37 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/chicken.svg b/launcher/resources/multimc/scalable/instances/chicken.svg new file mode 100644 index 00000000..83199722 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/chicken.svg @@ -0,0 +1,102 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/creeper.svg b/launcher/resources/multimc/scalable/instances/creeper.svg new file mode 100644 index 00000000..fdb90cab --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/creeper.svg @@ -0,0 +1,37 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/diamond.svg b/launcher/resources/multimc/scalable/instances/diamond.svg new file mode 100644 index 00000000..47133136 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/diamond.svg @@ -0,0 +1,30 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/dirt.svg b/launcher/resources/multimc/scalable/instances/dirt.svg new file mode 100644 index 00000000..45e4ec2a --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/dirt.svg @@ -0,0 +1,46 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/enderman.svg b/launcher/resources/multimc/scalable/instances/enderman.svg new file mode 100644 index 00000000..a13dde36 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/enderman.svg @@ -0,0 +1,60 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/enderpearl.svg b/launcher/resources/multimc/scalable/instances/enderpearl.svg new file mode 100644 index 00000000..ad88bcd5 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/enderpearl.svg @@ -0,0 +1,43 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/flame.svg b/launcher/resources/multimc/scalable/instances/flame.svg new file mode 100644 index 00000000..94d8cfff --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/flame.svg @@ -0,0 +1,30 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/fox.svg b/launcher/resources/multimc/scalable/instances/fox.svg index fcf16b2f..b1317b41 100644 --- a/launcher/resources/multimc/scalable/instances/fox.svg +++ b/launcher/resources/multimc/scalable/instances/fox.svg @@ -1,290 +1,113 @@ + + - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + viewBox="0 0 32 32" + id="svg168" + xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/ftb_logo.svg b/launcher/resources/multimc/scalable/instances/ftb_logo.svg new file mode 100644 index 00000000..7d553782 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/ftb_logo.svg @@ -0,0 +1,36 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/gear.svg b/launcher/resources/multimc/scalable/instances/gear.svg new file mode 100644 index 00000000..ca71778d --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/gear.svg @@ -0,0 +1,90 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/gold.svg b/launcher/resources/multimc/scalable/instances/gold.svg new file mode 100644 index 00000000..ad845612 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/gold.svg @@ -0,0 +1,30 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/grass.svg b/launcher/resources/multimc/scalable/instances/grass.svg new file mode 100644 index 00000000..184b2583 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/grass.svg @@ -0,0 +1,52 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/herobrine.svg b/launcher/resources/multimc/scalable/instances/herobrine.svg new file mode 100644 index 00000000..b58745c6 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/herobrine.svg @@ -0,0 +1,96 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/iron.svg b/launcher/resources/multimc/scalable/instances/iron.svg new file mode 100644 index 00000000..f67ee37b --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/iron.svg @@ -0,0 +1,30 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/magitech.svg b/launcher/resources/multimc/scalable/instances/magitech.svg new file mode 100644 index 00000000..9dd99390 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/magitech.svg @@ -0,0 +1,55 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/meat.svg b/launcher/resources/multimc/scalable/instances/meat.svg new file mode 100644 index 00000000..3cefa7e2 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/meat.svg @@ -0,0 +1,56 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/modrinth.svg b/launcher/resources/multimc/scalable/instances/modrinth.svg index a40f0e72..59df86b6 100644 --- a/launcher/resources/multimc/scalable/instances/modrinth.svg +++ b/launcher/resources/multimc/scalable/instances/modrinth.svg @@ -1,4 +1,41 @@ - - - - + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/netherstar.svg b/launcher/resources/multimc/scalable/instances/netherstar.svg new file mode 100644 index 00000000..dde8bddc --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/netherstar.svg @@ -0,0 +1,33 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/planks.svg b/launcher/resources/multimc/scalable/instances/planks.svg new file mode 100644 index 00000000..4328fec2 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/planks.svg @@ -0,0 +1,30 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/skeleton.svg b/launcher/resources/multimc/scalable/instances/skeleton.svg new file mode 100644 index 00000000..d8beb49b --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/skeleton.svg @@ -0,0 +1,69 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/squarecreeper.svg b/launcher/resources/multimc/scalable/instances/squarecreeper.svg new file mode 100644 index 00000000..71a28e12 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/squarecreeper.svg @@ -0,0 +1,49 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/steve.svg b/launcher/resources/multimc/scalable/instances/steve.svg new file mode 100644 index 00000000..0f29687c --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/steve.svg @@ -0,0 +1,108 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/stone.svg b/launcher/resources/multimc/scalable/instances/stone.svg new file mode 100644 index 00000000..c2863469 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/stone.svg @@ -0,0 +1,30 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/tnt.svg b/launcher/resources/multimc/scalable/instances/tnt.svg new file mode 100644 index 00000000..841f4ef8 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/tnt.svg @@ -0,0 +1,95 @@ + + + +Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher From e12a7a44156278799dee57dff9903a7917aaa7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20C=C3=A9zar?= Date: Sun, 20 Nov 2022 16:40:34 -0300 Subject: [PATCH 163/277] gradients! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- .../multimc/scalable/instances/bee.svg | 87 +++++++--- .../multimc/scalable/instances/brick.svg | 56 ++++-- .../multimc/scalable/instances/chicken.svg | 153 ++++++++++------- .../multimc/scalable/instances/creeper.svg | 49 +++++- .../multimc/scalable/instances/diamond.svg | 44 ++++- .../multimc/scalable/instances/dirt.svg | 54 +++--- .../multimc/scalable/instances/enderman.svg | 44 ++++- .../multimc/scalable/instances/enderpearl.svg | 81 +++++++-- .../multimc/scalable/instances/flame.svg | 26 ++- .../multimc/scalable/instances/fox.svg | 121 +++++++++---- .../multimc/scalable/instances/ftb_logo.svg | 67 ++++++-- .../multimc/scalable/instances/gear.svg | 104 +++++------- .../multimc/scalable/instances/gold.svg | 45 ++++- .../multimc/scalable/instances/grass.svg | 86 +++++++--- .../multimc/scalable/instances/herobrine.svg | 92 ++++++---- .../multimc/scalable/instances/iron.svg | 160 +++++++++++++++++- .../multimc/scalable/instances/magitech.svg | 84 ++++++--- .../multimc/scalable/instances/meat.svg | 51 +++++- .../multimc/scalable/instances/modrinth.svg | 37 +++- .../multimc/scalable/instances/netherstar.svg | 37 +++- .../multimc/scalable/instances/planks.svg | 75 +++++++- .../multimc/scalable/instances/skeleton.svg | 81 ++++++++- .../scalable/instances/squarecreeper.svg | 80 ++++++--- .../multimc/scalable/instances/steve.svg | 158 +++++++++++------ .../multimc/scalable/instances/stone.svg | 37 +++- .../multimc/scalable/instances/tnt.svg | 159 ++++++++++------- 26 files changed, 1517 insertions(+), 551 deletions(-) diff --git a/launcher/resources/multimc/scalable/instances/bee.svg b/launcher/resources/multimc/scalable/instances/bee.svg index a0870984..fef2aeb3 100644 --- a/launcher/resources/multimc/scalable/instances/bee.svg +++ b/launcher/resources/multimc/scalable/instances/bee.svg @@ -14,21 +14,54 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + xlink:href="#linearGradient1308" + id="linearGradient1310" + x1="16" + y1="27" + x2="16" + y2="5" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient1308"><stop + style="stop-color:#f3db6c;stop-opacity:1;" + offset="0" + id="stop1304" /><stop + style="stop-color:#ffeea9;stop-opacity:1;" + offset="1" + id="stop1306" /></linearGradient><linearGradient + xlink:href="#linearGradient1440" + id="linearGradient1442" + x1="7" + y1="24" + x2="11" + y2="14" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient1440"><stop + style="stop-color:#2c251f;stop-opacity:1;" + offset="0" + id="stop1436" /><stop + style="stop-color:#4d3f33;stop-opacity:1;" + offset="1" + id="stop1438" /></linearGradient><linearGradient + xlink:href="#linearGradient1460" + id="linearGradient1462" + x1="10" + y1="18" + x2="12" + y2="14" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient1460"><stop + style="stop-color:#4c7aba;stop-opacity:1;" + offset="0" + id="stop1456" /><stop + style="stop-color:#86c3cf;stop-opacity:1;" + offset="1" + id="stop1458" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + id="linearGradient288023"><stop + style="stop-color:#c1c1c1;stop-opacity:1;" + offset="0" + id="stop288019" /><stop + style="stop-color:#dfdfdf;stop-opacity:1;" + offset="1" + id="stop288021" /></linearGradient><linearGradient + xlink:href="#linearGradient84376" + id="linearGradient84368" + x1="48" + y1="26" + x2="48" + y2="6" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient84376"><stop + style="stop-color:#a63649;stop-opacity:1;" + offset="0" + id="stop84370" /><stop + style="stop-color:#df6277;stop-opacity:1;" + offset="1" + id="stop84381" /></linearGradient><linearGradient + xlink:href="#linearGradient288023" + id="linearGradient85182" + x1="48" + y1="6" + x2="48" + y2="26" + gradientUnits="userSpaceOnUse" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/chicken.svg b/launcher/resources/multimc/scalable/instances/chicken.svg index 83199722..30929c9e 100644 --- a/launcher/resources/multimc/scalable/instances/chicken.svg +++ b/launcher/resources/multimc/scalable/instances/chicken.svg @@ -8,43 +8,92 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + id="linearGradient2085"><stop + style="stop-color:#261a0a;stop-opacity:1;" + offset="0" + id="stop2081" /><stop + style="stop-color:#3c2b13;stop-opacity:1;" + offset="1" + id="stop2083" /></linearGradient><linearGradient + xlink:href="#linearGradient292700" + id="linearGradient292686" + x1="86.052681" + y1="26.999552" + x2="86" + y2="5" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient292700"><stop + style="stop-color:#d0d0d0;stop-opacity:1;" + offset="0.23078403" + id="stop292702" /><stop + style="stop-color:#eeeeee;stop-opacity:1;" + offset="0.83153141" + id="stop292698" /></linearGradient><linearGradient + xlink:href="#linearGradient293074" + id="linearGradient293076" + x1="80" + y1="31" + x2="80" + y2="21" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient293074"><stop + style="stop-color:#a63649;stop-opacity:1;" + offset="0" + id="stop293070" /><stop + style="stop-color:#df6277;stop-opacity:1;" + offset="0.52521378" + id="stop293078" /><stop + style="stop-color:#a63649;stop-opacity:1;" + offset="1" + id="stop293072" /></linearGradient><linearGradient + xlink:href="#linearGradient2085" + id="linearGradient42830" + x1="77" + y1="17" + x2="77" + y2="14" + gradientUnits="userSpaceOnUse" /><linearGradient + xlink:href="#linearGradient292039" + id="linearGradient292041" + x1="89" + y1="25" + x2="89" + y2="18" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient292039"><stop + style="stop-color:#fb9168;stop-opacity:1;" + offset="0" + id="stop292035" /><stop + style="stop-color:#f3db6c;stop-opacity:1;" + offset="1" + id="stop292037" /></linearGradient><linearGradient + xlink:href="#linearGradient2085" + id="linearGradient82810" + gradientUnits="userSpaceOnUse" + x1="77" + y1="17" + x2="77" + y2="14" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/creeper.svg b/launcher/resources/multimc/scalable/instances/creeper.svg index fdb90cab..38f7ac7a 100644 --- a/launcher/resources/multimc/scalable/instances/creeper.svg +++ b/launcher/resources/multimc/scalable/instances/creeper.svg @@ -8,27 +8,58 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + xlink:href="#linearGradient11855" + id="linearGradient11859" + x1="111" + y1="25" + x2="111" + y2="7" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient11855"><stop + style="stop-color:#57965f;stop-opacity:1;" + offset="0" + id="stop11851" /><stop + style="stop-color:#78bf6e;stop-opacity:1;" + offset="1" + id="stop11853" /></linearGradient><radialGradient + xlink:href="#linearGradient10455" + id="radialGradient10457" + cx="112" + cy="17" + fx="112" + fy="17" + r="6" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1666668,-6.8921104e-7,5.6666669e-7,1.1666667,-18.666684,-2.8332561)" /><linearGradient + id="linearGradient10455"><stop + style="stop-color:#1b2719;stop-opacity:1;" + offset="0" + id="stop10451" /><stop + style="stop-color:#0f150e;stop-opacity:1;" + offset="1" + id="stop10453" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + id="linearGradient41693"><stop + style="stop-color:#64d5df;stop-opacity:1;" + offset="0" + id="stop41689" /><stop + style="stop-color:#17c2d6;stop-opacity:1;" + offset="0.35665122" + id="stop41711" /><stop + style="stop-color:#89edf6;stop-opacity:1;" + offset="0.71356344" + id="stop41709" /><stop + style="stop-color:#2bc4d4;stop-opacity:1;" + offset="1" + id="stop41691" /></linearGradient><linearGradient + xlink:href="#linearGradient41693" + id="linearGradient2973" + gradientUnits="userSpaceOnUse" + x1="153" + y1="25" + x2="135" + y2="7" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/dirt.svg b/launcher/resources/multimc/scalable/instances/dirt.svg index 45e4ec2a..fe06bffc 100644 --- a/launcher/resources/multimc/scalable/instances/dirt.svg +++ b/launcher/resources/multimc/scalable/instances/dirt.svg @@ -14,33 +14,39 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + id="linearGradient53203"><stop + style="stop-color:#77563b;stop-opacity:1;" + offset="0" + id="stop53199" /><stop + style="stop-color:#86674f;stop-opacity:1;" + offset="1" + id="stop53201" /></linearGradient><linearGradient + xlink:href="#linearGradient53203" + id="linearGradient39079" + gradientUnits="userSpaceOnUse" + x1="785" + y1="26" + x2="785" + y2="6" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + x="768" + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/enderman.svg b/launcher/resources/multimc/scalable/instances/enderman.svg index a13dde36..483a002e 100644 --- a/launcher/resources/multimc/scalable/instances/enderman.svg +++ b/launcher/resources/multimc/scalable/instances/enderman.svg @@ -8,32 +8,68 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + xlink:href="#linearGradient5295" + id="linearGradient5297" + x1="239" + y1="5" + x2="239" + y2="27" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient5295"><stop + style="stop-color:#25262d;stop-opacity:1;" + offset="0" + id="stop5291" /><stop + style="stop-color:#141519;stop-opacity:1;" + offset="1" + id="stop5293" /></linearGradient><linearGradient + xlink:href="#linearGradient5303" + id="linearGradient5316" + x1="243" + y1="17" + x2="243" + y2="20" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient5303"><stop + style="stop-color:#bd44b3;stop-opacity:1;" + offset="0" + id="stop5299" /><stop + style="stop-color:#d84ecd;stop-opacity:1;" + offset="1" + id="stop5301" /></linearGradient><linearGradient + xlink:href="#linearGradient5303" + id="linearGradient5305" + x1="236" + y1="17" + x2="236" + y2="20" + gradientUnits="userSpaceOnUse" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + id="linearGradient3853"><stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="0.39989081" + id="stop3851" /><stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="1" + id="stop3849" /></linearGradient><linearGradient + id="linearGradient3405"><stop + style="stop-color:#27414e;stop-opacity:1;" + offset="0.16041158" + id="stop3401" /><stop + style="stop-color:#27414e;stop-opacity:0.74901961;" + offset="0.50162286" + id="stop39443" /><stop + style="stop-color:#27414e;stop-opacity:0;" + offset="1" + id="stop3403" /></linearGradient><linearGradient + id="linearGradient3000"><stop + style="stop-color:#62b397;stop-opacity:1;" + offset="0.47770822" + id="stop2996" /><stop + style="stop-color:#3a7a81;stop-opacity:1;" + offset="1" + id="stop2998" /></linearGradient><radialGradient + xlink:href="#linearGradient3000" + id="radialGradient3002-5" + cx="272" + cy="16" + fx="272" + fy="16" + r="12" + gradientUnits="userSpaceOnUse" /><radialGradient + xlink:href="#linearGradient3405" + id="radialGradient3407-4" + cx="272" + cy="16" + fx="272" + fy="16" + r="5" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2,0,0,1.2,-54.4,-3.2)" /><radialGradient + xlink:href="#linearGradient3853" + id="radialGradient3855-4" + cx="272" + cy="16" + fx="272" + fy="16" + r="5" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2,0,0,2,-326.4,-16)" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + xlink:href="#linearGradient1468" + id="linearGradient1470" + x1="300" + y1="26" + x2="300" + y2="10" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient1468"><stop + style="stop-color:#d63954;stop-opacity:1;" + offset="0" + id="stop1464" /><stop + style="stop-color:#e6812b;stop-opacity:1;" + offset="1" + id="stop1466" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/fox.svg b/launcher/resources/multimc/scalable/instances/fox.svg index b1317b41..687a5b09 100644 --- a/launcher/resources/multimc/scalable/instances/fox.svg +++ b/launcher/resources/multimc/scalable/instances/fox.svg @@ -14,55 +14,112 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"></clipPath><linearGradient + xlink:href="#linearGradient287187" + id="linearGradient287189" + x1="192.38519" + y1="22.999998" + x2="192.38519" + y2="17.999998" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient287187"><stop + style="stop-color:#dfdfdf;stop-opacity:1;" + offset="0" + id="stop287183" /><stop + style="stop-color:#f3f3f3;stop-opacity:1;" + offset="1" + id="stop287185" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + xlink:href="#linearGradient12453" + id="linearGradient12455" + x1="351" + y1="26" + x2="351" + y2="10" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient12453"><stop + style="stop-color:#b11917;stop-opacity:1;" + offset="0" + id="stop12449" /><stop + style="stop-color:#e65014;stop-opacity:1;" + offset="1" + id="stop12451" /></linearGradient><linearGradient + xlink:href="#linearGradient12299" + id="linearGradient12301" + x1="324" + y1="26" + x2="324" + y2="10" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient12299"><stop + style="stop-color:#0787c1;stop-opacity:1;" + offset="0" + id="stop12295" /><stop + style="stop-color:#65adb9;stop-opacity:1;" + offset="1" + id="stop12297" /></linearGradient><linearGradient + xlink:href="#linearGradient12445" + id="linearGradient12447" + x1="334" + y1="26" + x2="334" + y2="10" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient12445"><stop + style="stop-color:#798b2f;stop-opacity:1;" + offset="0" + id="stop12441" /><stop + style="stop-color:#9fc41e;stop-opacity:1;" + offset="1" + id="stop12443" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/gear.svg b/launcher/resources/multimc/scalable/instances/gear.svg index ca71778d..4bb91e08 100644 --- a/launcher/resources/multimc/scalable/instances/gear.svg +++ b/launcher/resources/multimc/scalable/instances/gear.svg @@ -8,80 +8,58 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><radialGradient + xlink:href="#linearGradient21157" + id="radialGradient21159" + cx="368" + cy="16" + fx="368" + fy="16" + r="7" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient21157"><stop + style="stop-color:#e1edf2;stop-opacity:1;" + offset="0" + id="stop21153" /><stop + style="stop-color:#abbdc4;stop-opacity:1;" + offset="0.72112602" + id="stop21161" /><stop + style="stop-color:#95acb6;stop-opacity:1;" + offset="1" + id="stop21155" /></linearGradient><radialGradient + xlink:href="#linearGradient21167" + id="radialGradient21309" + gradientUnits="userSpaceOnUse" + cx="400" + cy="16" + fx="400" + fy="16" + r="11" + gradientTransform="matrix(1.3636364,0,0,1.3636364,-177.45455,-5.8181818)" /><linearGradient + id="linearGradient21167"><stop + style="stop-color:#e5e6e9;stop-opacity:1;" + offset="0.13595749" + id="stop21163" /><stop + style="stop-color:#c5c7cf;stop-opacity:1;" + offset="0.86267382" + id="stop21165" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + id="linearGradient48524"><stop + style="stop-color:#dfcd64;stop-opacity:1;" + offset="0" + id="stop48516" /><stop + style="stop-color:#d6b917;stop-opacity:1;" + offset="0.11879402" + id="stop48518" /><stop + style="stop-color:#f6e689;stop-opacity:1;" + offset="0.71356344" + id="stop48520" /><stop + style="stop-color:#d4bb2b;stop-opacity:1;" + offset="1" + id="stop48522" /></linearGradient><linearGradient + xlink:href="#linearGradient48524" + id="linearGradient48512" + gradientUnits="userSpaceOnUse" + x1="153" + y1="25" + x2="135" + y2="7" + gradientTransform="translate(256)" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/grass.svg b/launcher/resources/multimc/scalable/instances/grass.svg index 184b2583..d33dad50 100644 --- a/launcher/resources/multimc/scalable/instances/grass.svg +++ b/launcher/resources/multimc/scalable/instances/grass.svg @@ -14,39 +14,71 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + id="linearGradient53203"><stop + style="stop-color:#77563b;stop-opacity:1;" + offset="0" + id="stop53199" /><stop + style="stop-color:#86674f;stop-opacity:1;" + offset="1" + id="stop53201" /></linearGradient><linearGradient + id="linearGradient29505"><stop + style="stop-color:#99cd61;stop-opacity:1;" + offset="0" + id="stop29501" /><stop + style="stop-color:#bccd61;stop-opacity:1;" + offset="1" + id="stop29503" /></linearGradient><linearGradient + xlink:href="#linearGradient53203" + id="linearGradient56913" + gradientUnits="userSpaceOnUse" + x1="785" + y1="26" + x2="785" + y2="6" + gradientTransform="translate(-352)" /><linearGradient + xlink:href="#linearGradient56992" + id="linearGradient56984" + x1="433" + y1="11" + x2="433" + y2="20" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient56992"><stop + style="stop-color:#4d3f33;stop-opacity:0.5;" + offset="0" + id="stop56986" /><stop + style="stop-color:#4d3f33;stop-opacity:0;" + offset="1" + id="stop56990" /></linearGradient><linearGradient + xlink:href="#linearGradient29505" + id="linearGradient29507" + x1="428" + y1="14" + x2="428" + y2="4" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(2,2)" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/herobrine.svg b/launcher/resources/multimc/scalable/instances/herobrine.svg index b58745c6..4704eb15 100644 --- a/launcher/resources/multimc/scalable/instances/herobrine.svg +++ b/launcher/resources/multimc/scalable/instances/herobrine.svg @@ -8,55 +8,75 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + id="linearGradient2085"><stop + style="stop-color:#261a0a;stop-opacity:1;" + offset="0" + id="stop2081" /><stop + style="stop-color:#3c2b13;stop-opacity:1;" + offset="1" + id="stop2083" /></linearGradient><radialGradient + xlink:href="#linearGradient2066" + id="radialGradient2757-3" + cx="496.06177" + cy="17.211182" + fx="495.87827" + fy="18.730774" + r="11" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.5554022,0,0,1.2337313,-771.57556,-4.2669434)" /><linearGradient + id="linearGradient2066"><stop + style="stop-color:#ceb8aa;stop-opacity:1;" + offset="0.23876573" + id="stop2062" /><stop + style="stop-color:#b39888;stop-opacity:1;" + offset="0.51858544" + id="stop2064" /></linearGradient><linearGradient + xlink:href="#linearGradient2085" + id="linearGradient94741" + gradientUnits="userSpaceOnUse" + x1="504" + y1="15" + x2="504" + y2="5" /><linearGradient + xlink:href="#linearGradient2564" + id="linearGradient2566" + x1="466" + y1="27" + x2="466" + y2="22" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient2564"><stop + style="stop-color:#45362e;stop-opacity:1;" + offset="0" + id="stop2560" /><stop + style="stop-color:#59463c;stop-opacity:1;" + offset="1" + id="stop2562" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + id="linearGradient27992"><stop + style="stop-color:#c5c7cf;stop-opacity:1;" + offset="0" + id="stop27990" /><stop + style="stop-color:#d8d9e0;stop-opacity:1;" + offset="1" + id="stop27988" /></linearGradient><linearGradient + id="linearGradient56358"><stop + style="stop-color:#ededed;stop-opacity:1;" + offset="0" + id="stop56354" /><stop + style="stop-color:#f4f4f4;stop-opacity:1;" + offset="1" + id="stop56356" /></linearGradient><linearGradient + xlink:href="#linearGradient27992" + id="linearGradient17901" + x1="496" + y1="26" + x2="496" + y2="6" + gradientUnits="userSpaceOnUse" /><linearGradient + xlink:href="#linearGradient56358" + id="linearGradient18032" + gradientUnits="userSpaceOnUse" + x1="503" + y1="25" + x2="503" + y2="23" + gradientTransform="translate(0,-4)" /><linearGradient + xlink:href="#linearGradient56358" + id="linearGradient18124" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-8)" + x1="503" + y1="25" + x2="503" + y2="23" /><linearGradient + xlink:href="#linearGradient18539" + id="linearGradient18530" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-6)" + x1="503" + y1="25" + x2="503" + y2="23" /><linearGradient + id="linearGradient18539"><stop + style="stop-color:#27414e;stop-opacity:0;" + offset="0" + id="stop18537" /><stop + style="stop-color:#27414e;stop-opacity:0.74901961;" + offset="0.49837714" + id="stop18535" /><stop + style="stop-color:#27414e;stop-opacity:1;" + offset="0.83958842" + id="stop18533" /></linearGradient><linearGradient + xlink:href="#linearGradient18539" + id="linearGradient18590" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-10)" + x1="503" + y1="25" + x2="503" + y2="23" /><linearGradient + xlink:href="#linearGradient18539" + id="linearGradient18615" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-14)" + x1="503" + y1="25" + x2="503" + y2="23" /><linearGradient + xlink:href="#linearGradient18539" + id="linearGradient18640" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-2)" + x1="503" + y1="25" + x2="503" + y2="23" /><linearGradient + xlink:href="#linearGradient56358" + id="linearGradient18124-2" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-12)" + x1="503" + y1="25" + x2="503" + y2="23" /><linearGradient + xlink:href="#linearGradient56358" + id="linearGradient18124-0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(0,-16)" + x1="503" + y1="25" + x2="503" + y2="23" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/magitech.svg b/launcher/resources/multimc/scalable/instances/magitech.svg index 9dd99390..d866c6f0 100644 --- a/launcher/resources/multimc/scalable/instances/magitech.svg +++ b/launcher/resources/multimc/scalable/instances/magitech.svg @@ -8,45 +8,75 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><radialGradient + xlink:href="#linearGradient16441" + id="radialGradient16443" + cx="559.9212" + cy="16.022875" + fx="559.9212" + fy="16.022875" + r="11.80246" + gradientTransform="matrix(1,0,0,1.0119015,0,-0.19069696)" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient16441"><stop + style="stop-color:#9f0bff;stop-opacity:1;" + offset="0.4400529" + id="stop16439" /><stop + style="stop-color:#7111f8;stop-opacity:1;" + offset="1" + id="stop16437" /></linearGradient><radialGradient + xlink:href="#linearGradient14255" + id="radialGradient14257" + cx="560" + cy="16" + fx="560" + fy="16" + r="9" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-32)" /><linearGradient + id="linearGradient14255"><stop + style="stop-color:#e570f7;stop-opacity:1;" + offset="0" + id="stop14251" /><stop + style="stop-color:#d829c8;stop-opacity:1;" + offset="1" + id="stop14253" /></linearGradient><linearGradient + xlink:href="#linearGradient16452" + id="linearGradient16454" + x1="560" + y1="25" + x2="560" + y2="11" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-32)" /><linearGradient + id="linearGradient16452"><stop + style="stop-color:#f6cff4;stop-opacity:1;" + offset="0" + id="stop16450" /><stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="1" + id="stop16448" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + id="linearGradient6068"><stop + style="stop-color:#cdcdcd;stop-opacity:1;" + offset="0" + id="stop6064" /><stop + style="stop-color:#eeeeee;stop-opacity:1;" + offset="1" + id="stop6066" /></linearGradient><linearGradient + xlink:href="#linearGradient6068" + id="linearGradient39389" + x1="565" + y1="23" + x2="567" + y2="21" + gradientUnits="userSpaceOnUse" /><linearGradient + xlink:href="#linearGradient38100" + id="linearGradient38102" + x1="408.70773" + y1="-379.00925" + x2="408.70773" + y2="-389.8125" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient38100"><stop + style="stop-color:#956c4a;stop-opacity:1;" + offset="0" + id="stop38096" /><stop + style="stop-color:#bb7c47;stop-opacity:1;" + offset="1" + id="stop38098" /></linearGradient><linearGradient + xlink:href="#linearGradient39375" + id="linearGradient39377" + x1="401.63666" + y1="-379.00925" + x2="401.63666" + y2="-389.8125" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient39375"><stop + style="stop-color:#d15e65;stop-opacity:1;" + offset="0" + id="stop39371" /><stop + style="stop-color:#b2594e;stop-opacity:1;" + offset="1" + id="stop39373" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + id="linearGradient3108"><stop + style="stop-color:#1bd96a;stop-opacity:1;" + offset="0" + id="stop3104" /><stop + style="stop-color:#1bd9a1;stop-opacity:1;" + offset="1" + id="stop3106" /></linearGradient><linearGradient + xlink:href="#linearGradient3108" + id="linearGradient3110" + x1="70.852509" + y1="141.75883" + x2="70.852509" + y2="0.053809531" + gradientUnits="userSpaceOnUse" /><linearGradient + xlink:href="#linearGradient3108" + id="linearGradient50774" + gradientUnits="userSpaceOnUse" + x1="70.852509" + y1="141.75883" + x2="70.852509" + y2="0.053809531" /><linearGradient + xlink:href="#linearGradient3108" + id="linearGradient50776" + gradientUnits="userSpaceOnUse" + x1="70.852509" + y1="141.75883" + x2="70.852509" + y2="0.053809531" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<radialGradient + xlink:href="#linearGradient62774" + id="radialGradient62776" + cx="624" + cy="15" + fx="624" + fy="15" + r="12" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient62774"><stop + style="stop-color:#f2effd;stop-opacity:1;" + offset="0.45171013" + id="stop62772" /><stop + style="stop-color:#d2cbf3;stop-opacity:1;" + offset="1" + id="stop62770" /></linearGradient><radialGradient + xlink:href="#linearGradient62766" + id="radialGradient62768" + cx="624" + cy="16" + fx="624" + fy="16" + r="6" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient62766"><stop + style="stop-color:#fafbb9;stop-opacity:1;" + offset="0" + id="stop62762" /><stop + style="stop-color:#fafbb9;stop-opacity:0;" + offset="1" + id="stop62764" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + xlink:href="#linearGradient19391" + id="linearGradient19373" + x1="690" + y1="22" + x2="690" + y2="21" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient19391"><stop + style="stop-color:#cfaf6b;stop-opacity:1;" + offset="0" + id="stop19387" /><stop + style="stop-color:#ddc694;stop-opacity:1;" + offset="1" + id="stop19389" /></linearGradient><linearGradient + xlink:href="#linearGradient19391" + id="linearGradient19435" + gradientUnits="userSpaceOnUse" + x1="690" + y1="22" + x2="690" + y2="21" + gradientTransform="translate(0,-5)" /><linearGradient + xlink:href="#linearGradient19391" + id="linearGradient19458" + gradientUnits="userSpaceOnUse" + x1="690" + y1="22" + x2="690" + y2="21" + gradientTransform="translate(0,-10)" /><linearGradient + xlink:href="#linearGradient19391" + id="linearGradient19481" + gradientUnits="userSpaceOnUse" + x1="690" + y1="22" + x2="690" + y2="21" + gradientTransform="translate(0,-15)" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/skeleton.svg b/launcher/resources/multimc/scalable/instances/skeleton.svg index d8beb49b..33a34abb 100644 --- a/launcher/resources/multimc/scalable/instances/skeleton.svg +++ b/launcher/resources/multimc/scalable/instances/skeleton.svg @@ -8,46 +8,112 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + xlink:href="#linearGradient6068" + id="linearGradient6070" + x1="656" + y1="27" + x2="656" + y2="5" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient6068"><stop + style="stop-color:#cdcdcd;stop-opacity:1;" + offset="0" + id="stop6064" /><stop + style="stop-color:#eeeeee;stop-opacity:1;" + offset="1" + id="stop6066" /></linearGradient><linearGradient + xlink:href="#linearGradient7624" + id="linearGradient7626" + x1="658" + y1="20" + x2="658" + y2="24" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient7624"><stop + style="stop-color:#8e8e86;stop-opacity:1;" + offset="0" + id="stop7620" /><stop + style="stop-color:#8e8e86;stop-opacity:0;" + offset="1" + id="stop7622" /></linearGradient><linearGradient + xlink:href="#linearGradient7155" + id="linearGradient7165" + x1="656" + y1="25" + x2="656" + y2="22" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient7155"><stop + style="stop-color:#3e4447;stop-opacity:1;" + offset="0" + id="stop7151" /><stop + style="stop-color:#2e3134;stop-opacity:1;" + offset="1" + id="stop7153" /></linearGradient><radialGradient + xlink:href="#linearGradient6078" + id="radialGradient6080" + cx="662" + cy="18" + fx="662" + fy="18" + r="3" + gradientTransform="matrix(0.99999794,-1.7103091e-6,1.7879388e-6,1.0000015,0.0013367,0.00110992)" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient6078"><stop + style="stop-color:#006868;stop-opacity:1;" + offset="0" + id="stop6074" /><stop + style="stop-color:#3e4447;stop-opacity:1;" + offset="1" + id="stop6076" /></linearGradient><linearGradient + xlink:href="#linearGradient7155" + id="linearGradient7157" + x1="651" + y1="20" + x2="651" + y2="16" + gradientUnits="userSpaceOnUse" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<radialGradient + xlink:href="#linearGradient36865" + id="radialGradient10457-1" + cx="112" + cy="17" + fx="112" + fy="17" + r="6" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1666668,-6.8921104e-7,5.6666669e-7,1.1666667,589.33332,-2.8332561)" /><linearGradient + id="linearGradient36865"><stop + style="stop-color:#63271f;stop-opacity:1;" + offset="0" + id="stop36861" /><stop + style="stop-color:#3d1212;stop-opacity:1;" + offset="1" + id="stop36863" /></linearGradient><linearGradient + xlink:href="#linearGradient36859" + id="linearGradient11859-1" + x1="111" + y1="25" + x2="111" + y2="7" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(608)" /><linearGradient + id="linearGradient36859"><stop + style="stop-color:#729657;stop-opacity:1;" + offset="0" + id="stop36855" /><stop + style="stop-color:#a5bf6e;stop-opacity:1;" + offset="1" + id="stop36857" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/steve.svg b/launcher/resources/multimc/scalable/instances/steve.svg index 0f29687c..53b35b6e 100644 --- a/launcher/resources/multimc/scalable/instances/steve.svg +++ b/launcher/resources/multimc/scalable/instances/steve.svg @@ -8,55 +8,101 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><radialGradient + xlink:href="#linearGradient2739" + id="radialGradient2757" + cx="496.06177" + cy="17.211182" + fx="495.87827" + fy="18.730774" + r="11" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.5554022,0,0,1.2337313,-515.57556,-4.2669434)" /><linearGradient + id="linearGradient2739"><stop + style="stop-color:#cca997;stop-opacity:1;" + offset="0.23748928" + id="stop2755" /><stop + style="stop-color:#bd8e74;stop-opacity:1;" + offset="0.51858547" + id="stop2735" /></linearGradient><linearGradient + xlink:href="#linearGradient1330" + id="linearGradient1332" + x1="487" + y1="15" + x2="487" + y2="5" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(256)" /><linearGradient + id="linearGradient1330"><stop + style="stop-color:#261a0a;stop-opacity:1;" + offset="0" + id="stop1326" /><stop + style="stop-color:#422e11;stop-opacity:1;" + offset="1" + id="stop1328" /></linearGradient><linearGradient + xlink:href="#linearGradient1463" + id="linearGradient1465" + x1="755" + y1="27" + x2="755" + y2="22" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient1463"><stop + style="stop-color:#45220e;stop-opacity:1;" + offset="0" + id="stop1459" /><stop + style="stop-color:#552910;stop-opacity:1;" + offset="1" + id="stop1461" /></linearGradient><linearGradient + xlink:href="#linearGradient1503" + id="linearGradient1505" + x1="748" + y1="20" + x2="748" + y2="17" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient1503"><stop + style="stop-color:#5c3874;stop-opacity:1;" + offset="0" + id="stop1499" /><stop + style="stop-color:#3c3874;stop-opacity:1;" + offset="1" + id="stop1501" /></linearGradient><linearGradient + xlink:href="#linearGradient1503" + id="linearGradient1365" + gradientUnits="userSpaceOnUse" + x1="748" + y1="20" + x2="748" + y2="17" /><linearGradient + xlink:href="#linearGradient1503" + id="linearGradient1367" + gradientUnits="userSpaceOnUse" + x1="748" + y1="20" + x2="748" + y2="17" /></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher<linearGradient + xlink:href="#linearGradient25803" + id="linearGradient25805" + x1="785" + y1="26" + x2="785" + y2="6" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient25803"><stop + style="stop-color:#8e8e86;stop-opacity:1;" + offset="0" + id="stop25799" /><stop + style="stop-color:#a2a29b;stop-opacity:1;" + offset="1" + id="stop25801" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="0" /> diff --git a/launcher/resources/multimc/scalable/instances/tnt.svg b/launcher/resources/multimc/scalable/instances/tnt.svg index 841f4ef8..a7a2689f 100644 --- a/launcher/resources/multimc/scalable/instances/tnt.svg +++ b/launcher/resources/multimc/scalable/instances/tnt.svg @@ -8,85 +8,116 @@ viewBox="0 0 32 32" id="svg168" xml:space="preserve" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"><linearGradient + xlink:href="#linearGradient18899" + id="linearGradient18901" + x1="805" + y1="16" + x2="812" + y2="16" + gradientUnits="userSpaceOnUse" /><linearGradient + id="linearGradient18899"><stop + style="stop-color:#bf454c;stop-opacity:1;" + offset="0" + id="stop18895" /><stop + style="stop-color:#df6277;stop-opacity:1;" + offset="0.5" + id="stop18972" /><stop + style="stop-color:#bf454c;stop-opacity:1;" + offset="1" + id="stop18897" /></linearGradient><linearGradient + xlink:href="#linearGradient18899" + id="linearGradient19011" + x1="812" + y1="16" + x2="820" + y2="16" + gradientUnits="userSpaceOnUse" /><linearGradient + xlink:href="#linearGradient18899" + id="linearGradient19015" + gradientUnits="userSpaceOnUse" + x1="805" + y1="16" + x2="812" + y2="16" + gradientTransform="matrix(-1,0,0,1,1632,0)" /><linearGradient + xlink:href="#linearGradient19917" + id="linearGradient22848" + gradientUnits="userSpaceOnUse" + x1="816" + y1="27" + x2="816" + y2="20" /><linearGradient + id="linearGradient19917"><stop + style="stop-color:#a02722;stop-opacity:1;" + offset="0" + id="stop19913" /><stop + style="stop-color:#a02722;stop-opacity:0;" + offset="1" + id="stop19915" /></linearGradient><radialGradient + xlink:href="#linearGradient19935" + id="radialGradient19929" + cx="816" + cy="14" + fx="816" + fy="14" + r="11" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1570249,0,0,1,-128.13223,0)" /><linearGradient + id="linearGradient19935"><stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop19933" /><stop + style="stop-color:#f6cff4;stop-opacity:1;" + offset="1" + id="stop19931" /></linearGradient></defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Date: Tue, 22 Nov 2022 13:02:39 -0300 Subject: [PATCH 164/277] merge with upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- launcher/resources/instance_icons.svg | 2925 ++++++++++++----- .../128x128/instances/chicken_legacy.png | Bin 0 -> 6369 bytes .../128x128/instances/creeper_legacy.png | Bin 0 -> 9046 bytes .../128x128/instances/enderpearl_legacy.png | Bin 0 -> 21425 bytes .../128x128/instances/flame_legacy.png | Bin 0 -> 6226 bytes .../128x128/instances/ftb_glow_legacy.png | Bin 0 -> 12708 bytes .../128x128/instances/ftb_logo_legacy.png | Bin 0 -> 7883 bytes .../multimc/128x128/instances/gear_legacy.png | Bin 0 -> 18321 bytes .../128x128/instances/herobrine_legacy.png | Bin 0 -> 4937 bytes .../128x128/instances/infinity_legacy.png | Bin 0 -> 8012 bytes .../128x128/instances/magitech_legacy.png | Bin 0 -> 23097 bytes .../multimc/128x128/instances/meat_legacy.png | Bin 0 -> 10583 bytes .../128x128/instances/netherstar_legacy.png | Bin 0 -> 14062 bytes .../128x128/instances/skeleton_legacy.png | Bin 0 -> 3673 bytes .../instances/squarecreeper_legacy.png | Bin 0 -> 9136 bytes .../128x128/instances/steve_legacy.png | Bin 0 -> 4312 bytes .../multimc/32x32/instances/brick_legacy.png | Bin 0 -> 2388 bytes .../32x32/instances/chicken_legacy.png | Bin 0 -> 1181 bytes .../32x32/instances/creeper_legacy.png | Bin 0 -> 1524 bytes .../32x32/instances/diamond_legacy.png | Bin 0 -> 2444 bytes .../multimc/32x32/instances/dirt_legacy.png | Bin 0 -> 482 bytes .../32x32/instances/enderpearl_legacy.png | Bin 0 -> 2120 bytes .../multimc/32x32/instances/ftb_glow.png | Bin 0 -> 1747 bytes .../32x32/instances/ftb_logo_legacy.png | Bin 0 -> 1607 bytes .../multimc/32x32/instances/gear_legacy.png | Bin 0 -> 2414 bytes .../multimc/32x32/instances/gold_legacy.png | Bin 0 -> 2366 bytes .../multimc/32x32/instances/grass_legacy.png | Bin 0 -> 618 bytes .../32x32/instances/herobrine_legacy.png | Bin 0 -> 1034 bytes .../32x32/instances/infinity_legacy.png | Bin 0 -> 1714 bytes .../multimc/32x32/instances/iron_legacy.png | Bin 0 -> 1772 bytes .../32x32/instances/magitech_legacy.png | Bin 0 -> 2646 bytes .../multimc/32x32/instances/meat_legacy.png | Bin 0 -> 1514 bytes .../32x32/instances/netherstar_legacy.png | Bin 0 -> 1942 bytes .../multimc/32x32/instances/planks_legacy.png | Bin 0 -> 2299 bytes .../32x32/instances/skeleton_legacy.png | Bin 0 -> 696 bytes .../32x32/instances/squarecreeper_legacy.png | Bin 0 -> 1623 bytes .../multimc/32x32/instances/steve_legacy.png | Bin 0 -> 969 bytes .../multimc/32x32/instances/stone_legacy.png | Bin 0 -> 1866 bytes .../multimc/32x32/instances/tnt_legacy.png | Bin 0 -> 378 bytes .../50x50/instances/enderman_legacy.png | Bin 0 -> 2429 bytes launcher/resources/multimc/multimc.qrc | 71 +- .../multimc/scalable/instances/bee_legacy.svg | 159 + .../multimc/scalable/instances/fox.svg | 45 +- .../multimc/scalable/instances/fox_legacy.svg | 290 ++ .../multimc/scalable/instances/meat.svg | 2 +- 45 files changed, 2645 insertions(+), 847 deletions(-) create mode 100644 launcher/resources/multimc/128x128/instances/chicken_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/creeper_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/enderpearl_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/flame_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/ftb_glow_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/ftb_logo_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/gear_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/herobrine_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/infinity_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/magitech_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/meat_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/netherstar_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/skeleton_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/squarecreeper_legacy.png create mode 100644 launcher/resources/multimc/128x128/instances/steve_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/brick_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/chicken_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/creeper_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/diamond_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/dirt_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/enderpearl_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/ftb_glow.png create mode 100644 launcher/resources/multimc/32x32/instances/ftb_logo_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/gear_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/gold_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/grass_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/herobrine_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/infinity_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/iron_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/magitech_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/meat_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/netherstar_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/planks_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/skeleton_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/squarecreeper_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/steve_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/stone_legacy.png create mode 100644 launcher/resources/multimc/32x32/instances/tnt_legacy.png create mode 100644 launcher/resources/multimc/50x50/instances/enderman_legacy.png create mode 100644 launcher/resources/multimc/scalable/instances/bee_legacy.svg create mode 100644 launcher/resources/multimc/scalable/instances/fox_legacy.svg diff --git a/launcher/resources/instance_icons.svg b/launcher/resources/instance_icons.svg index 0c2b9e20..84c0ef6f 100644 --- a/launcher/resources/instance_icons.svg +++ b/launcher/resources/instance_icons.svg @@ -384,7 +384,1389 @@ height="22" x="485" y="5" - ry="3" />Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism LauncherTODOTODOTODOTODOTODOTODO?TODOTODO + y="32" /> diff --git a/launcher/resources/multimc/128x128/instances/chicken_legacy.png b/launcher/resources/multimc/128x128/instances/chicken_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..71f6dedc54f470a6f8219a427bbabbae1f025a02 GIT binary patch literal 6369 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 V$PTXg^yBXcz{2z*w#gWm^na^=xRC$= literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/128x128/instances/creeper_legacy.png b/launcher/resources/multimc/128x128/instances/creeper_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..41b7d07dbfcc841eb6dded16284b1a49fe4e8b4a GIT binary patch literal 9046 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 literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/128x128/instances/enderpearl_legacy.png b/launcher/resources/multimc/128x128/instances/enderpearl_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..0a5bf91a440a6a2a6cb303c7b551b6c7976307fb GIT binary patch literal 21425 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 QQvd(}07*qoM6N<$f>i5V+5i9m literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/128x128/instances/flame_legacy.png b/launcher/resources/multimc/128x128/instances/flame_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..6482975c494e02522f34a10894bb434f1ccc7194 GIT binary patch literal 6226 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 YzkS2@~ literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/128x128/instances/ftb_glow_legacy.png b/launcher/resources/multimc/128x128/instances/ftb_glow_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..86632b21ded4a18156a011115bdf671a8c998cce GIT binary patch literal 12708 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�jIYC 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 NfP#!Fyha)k@_&Gtop%5L literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/128x128/instances/gear_legacy.png b/launcher/resources/multimc/128x128/instances/gear_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..75c68a66f0dcc656fbd42028d313a469dd50dc85 GIT binary patch literal 18321 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

NaO6%(?ND*KfV(=Pzb7d7z8uVO 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 literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/128x128/instances/herobrine_legacy.png b/launcher/resources/multimc/128x128/instances/herobrine_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..13f1494c4dd62b54e2e16942b23036e2968d640d GIT binary patch literal 4937 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 O0000pdok@5If_ z3<2Ea_dWEG-LUV$2X8#t4o=f|U}xEP(r+rD_rGTa@Poj^_Z^`4_~SPMfBkmfef>=y zhR5$O?;m_nU|`=_MEH&%z>(4Z&_l+3_Zg2rZhYuNu>N)9sh5nG&lvXtm#)1!0d~&_ zz5&=5ItqEY59U1kT zn}Iu);r6{0mJDKIH~RQQ@YaKp2dbrMQ5(zF)ON=ZV2Y(ibgeANE#P5S9w*a$Z8^wH z*D@Ml*MWNvFRr+by~s@q55qX#a@zbk~O zzSR%#dbcAZ|NMuH`I&fev>E4f>U45YH(fVqqYYx4h87qY)+FU7234dGg-7Iu=YY#3 zS$|_JB#~V*Wy@p>w5Hs~s6|M==sYhpu7gigI5!I2m9I{Wlbe|rUlbQF3Lb$M-&O9n zxoMm}-GlrneEk9Ok@MpI`vo7xdiU~MO@wbN32@-%^U%?|dlFgCMKrsX_!GkaCc_kz{x>9LO0wyGhxdgs0YBiy)il?6Z*atg7pPzUzXR=Kx_`hWJ65PJExx#oA<0#2lRo8Zxs=~Z3u7! z_!sUA7oXa!=0oak*9UbShSBq=OFjwFCpp^7T9ZD$&dO>JlDij84)rI0({ z>XuHs=0Ij|&$;8WA0ooHj0E2j0vw3(*SoC@zM}KzbLwubZtC2P0_`}ZI5DwiV$I2G z=8T;@!Wo$722m+{Ma}{Dq5@J1NQMjC!6i$n5FpVyK9_uI@;xQ(hMf1jV=s6&OX?QZ zsUX)BSEDYSt$2G!Y^gAKBivxm9oP{kAnF>PJ*%wUk%+jA~ocJ8^yx&L9UY=qxQ zBK(dKpv2Jn?V*Q^2LT?0haZ0Rz|3kpf8N;G2)9F=t-V~gdkmT;4m-1PaMp~xZWh*a z_H{E&EHjbkk(_~09a6649&r@{WDh7?kpynI<1XRZNy<`g&3z$fYVLc=c~@x1Qr`3I z(~x{_p4u*Tnbb*L3Unc$HY$YxU~&htDR<_n3p8!WUANcGJ8BXvGu)op*l-?rzz=&A zzY|3GrW0Tv_=g@c9_#@>f8KZicy!%(Kk)cdHz&bkz&-bfr=Bv_Ubfp$-5M4fyVY`1 zHO+iB?69%5Y27TWTV?CGPI%V3d0unZ*wB#d14%XR74o2*BUy$kkW)YukHFo?4lZ}k z*<2m&OJ&U^-!tW1CG7_HUH5JpmOjs(Tb3ykvS?FOM+7340|}7aa!#1qlv!G8mm#nW zOD$*WTIN;9Xw;EX{#HnXZyEuP#O1-@VT1F=;~U}LX3bJHvQ!PsFI}}SknFYlf|ZPUMtNnsdTn&6OvXeJp8!t5NnU zxL1;g0=x%2_fo)(?B**1WPy?^lW=cMzEJj=vQN#mtDN`D)6}!iEqROQOxGDHN4YAb zsz?|>$+Og%v$T{KX3SET=fTt5k}V>>oaSZAAarba;`W`)#(C#~NBnn!1m6?_+!&rc zZ(P_2caP79_Uc~fMr$EA^Wb~Spjew?WeV4YW_Q4HlC6!!_BOUQmS4IWG#H1$&b;Y{ zJ_uc!m~1^|oiOE-$W9ts$9+v=tel5P19R1qA}LkK16f7y=Nyz2dLuC481AN=LGEOC zxf1S6%f68N49_zqO_lS^a$4ZI^Dbv3BjrjdS3#=4V-fz@kvhx1u&lW&PtBR;tf`r1 zw#>5-Sj;OH%y&CRbfoRf{PxWGN5sR3Z=$F0mJ{HJpU!>8qnqL0lXryKc8=|u$29kt zhwE|N=IB)ligoa13WV0Qj6DhFm^Z?+tZy?<8ZTP&dJyuoZDMGLDTRq)>z3M6;3u(g z!d>g8JdyjzvJJp%Wv}pD^@d*{dr+|f4CO+I>{g_L8*)55xXV0ecXgJ$MOrB3g(=UK zwIJoCdrsD6m*;?cBzcXzDz*lgyOY&9zA*Qh(Dg`NN{cqLo9tV+IIxGV-|xkTARotAY@HZ;o<%Q}HLndK)faRPiz^08$f zA~jn-`d9wU{umM#f4rA9f_!W*b7Op%l#IR=zXDVtT5ia#dN;lboa&Ll@6 zTOw4k(jaHX24kdAa1AjHBsD>*B~_AFZwCDSnu<&`sko5t4fsH=ggoPNTu2HgMUyIu z9S~eHlN?tzY3Z(oxlbgYm^3oiNS=q$)MO8}nODqGHHsGJVHQ|~mf^7D@yFqT2b@P9 zaX@BfZxspN5(3-+{^P{kx1Wl8X9iWvpdLhPmTekTHW;*M5KRLkH1f6)Rg`ii6d)RA zrkp^M1$Sk40-0Q3umo6$mb?in;JY+LK7`Onsu30P3Mafd@B#OT1dhxVg=$;_Ls^dZ zH#U=kWC5wdh&a&<6A~gz-kGzMG)5Xr8bKZz3_aT*;Em_pxT%(M9A?6zS_aYphC|rc zNIdWWZ#4lRcAjwm?P(N+BDyWsL+Q6GOQ*Ms9GgaYaEu9^MrAPzZp)bD8&biB-c32Evd6 zD1aia8e~YJa3H(8lF2){MkWo-eQ3^5uAzAz$kl{QljW6VMaXfe5Efx!bhQzq@%-~| zISJkr0bUXO$)Ij_8N^`2Hs(>N>k&DRA&kI>WFHDOxK$7$@`x-*W{^u%PUaLHn!z&3 zGq|jDcqJGV68ONNk*hMdaxYgg_}&co<-N&$@_>hcX9O~lg=fPDk_P4sB_E=qwoGb-n&8wfuiP!F%V7{KEb9&y!5EL>`R92n zN$`dV@Jiq>m777Pi7Ab`SdGlOF&@VR#zr0?8xrHda&9ClT$Mur4^j@R&NolOIo8kfTz>7@d6I3ryd#;4QL4I-Kn+qO_r_4;?ln>q zMCIA4AVqfqq~Hl7v_=A|0Sq_A#{H>;NecXwUisA+T@jTne96w z0d4^GChfTMT>V^uf+U;VcJ9QjKm7f7TT@pql~WKhM}iy-JP_aaZcEQ!8d?>HKDR~c z@{X`9$@`FW#=SDBR`&Aj?)wI;oDd+m+{mJq(S;V0L2Sfu6Yjr1@z`Smus4{R_PPnM zAKQQUKI5Lv16Pj&o!@1Rt*XZ6nhy+>O+Iw_Fc^)bN;%g-7H~zB!(Te{&+q>~ zH-Gf0EA6+@sv1xq{>&08W7H)E?ByMXa2ip=h_o``<4fee?~hg4Z1g>Iq=*NZ^Tiyk!=v zg>_96Z4k#{*d~q5ZDP&@*~CH*zH4O0K&X*a3$H>ZN*>8M{EhSf`v?B?sZadKQ|+xU zeE6yV;h$fbk4kve9e0P$tytd)U(MmC98HRNj%2S!ESo=e>*sHO;M9}1egFCkx7v%WD>@ zNfr|`ADM@dsaXAzYQrl;R4&%~-#GX8Kk%nded0$}-}B`1))zkfwZHmvSC->k4qfx6#r`Xt`}pkE?jLXc-sNZYUY_{c zwVi8^Ke?BeOJa!eZGZ6h+&a4J;s{|yJ;<*9!l#t>rb>Hrylst?NjtODZEx=>Wk>GH z*>QE|-r_!!`yOITo;OWwAm5O}zDV%I z;?@^G{FT4_FRpfzL`uQEw+pfiPhpK&^KkC7pQB&MP9b*Hm=S* zrB3c5dzmzHUn=_yX%}fruB$x>F3IdNnafuB)s(b_(k{a=C3Z{lxsJykTS zfQKIr++Rlj>}+c4Su+fiYQ#LOsj@Yaujjx7F)?Xu=0hno^4ypaaj#SiwD8mC{{9F5 z)QKCxU$rOZx4iWGAOB0gaIKpp?=yGSm{$+o`SaiXk#mpUe|6ei zd??&|`#f&9qk0kYpsw0c$Toqv4!#arC&UTziHQ4AF0*2!DSLb^;IG;@7Pq|o;V=JJ zAG@*~CGqmNp;P=PH=xFX)sj^vRRP}z zUn2bUnZNtNKXu~cKYHZ0uL9INhnH?Sdi>uN@Iej)BYPx;2sWgy0zqckp=lZ{1dI~k zBmwRwFE@}GW`V(-Z)+C6{d__x?X!yV&V&mRqLa}E(JIi&b6W+qDOiU>VmisRGv}%I zJ3N|S3!<-+01q47gU696nrT@Fsf*N#N>tP_(xwVoWk!@)kO)XYnElN&|Jw)u;Ghh!(S@ z#KK?!VNts;JLrfpvA&+!-p)LLcm#hnDe(GLz^Gqc8!MAVqg@6<4AFDMQ&bg#%S(0) zA(UA%3i#{4GvH6Jif6whp8dM>;&YIlgPderHSK@i+7?^8{mu5z6BgjM+u$Ad7;k@v z(G0Hpu6|XzyOBf1^VMBc7K&;&BVbg0pvVMPV#XMbyUJG?U?qfPF*3OGVqE_Avyx+l z1e6P~j7UhWU>T%^8w-@@SX|)V0xi`vvF4eL`vs33?x|z9Hxvrap)n8&OEWXWa-cR- zwlXCu?_>HoAnb3R{ktFhsr8Tlz9Tnzi@{Gz_{t;BlV1{7HgqH4huO6q+$ZFp1cq#CP zCxa9!g9w9Z*^#o91#c=z9d0Jf{>tf}eeX}LfBeUeUVKcu+5T4nUm3psZ=6RT5wqzb z;Nf_%x$d)Ff6R_O=7MKlhG#B`=5yi)zSsD!cNzJ)t}@lGXbs2dggf$a}Uf+HdD|X(*n1aYThwU znT`AX)ug~%k`bf={?VOYR^9#|+rR&bpBVqzb8oL-zN4IcOuO0sR{{Uo&x_A~%-P!1 zYX-Zn;NmG*9~t8TjE6AkU&n*;Gv*oQ-9ekR;?g!;+JWTICiwK{#n*lte%B8KCg&=W z4cM&TMr9Gha1-XB#iC4J6GJ+~R}I1lI6xejb@B+6?z76V*73m4BD@5i_@3;fpektZ(IeEF-lqPGo*3dMFa zu$1X`XQ<2Ml<;n)|AqE7O)vUoy{#AadoK(IRA*1e?&`GzDS!ro5n>I~T-b>Kdyegr z*~2tNVNR@<>5u-i zCzrRr@J7M^`XkOS{=>{-b__`UdkgUHTa2H4zwyDh_u#)0*!vg0HWI9iL(Dk69{Ang zUGZZd7*N0cK7vIm3sfOc5y;aSTDJJI!&}F5#*>%dR5FSC->Uv|*}r0dBxanx_WB6$aDP!gE6bBoO{0#E zN4G%6J>z5~FDq+$W#>g5TuGDk;ivxl|L@86))!w7_{#9>Kb!frpYy_}n}C1k8Tg6! z89)3^7!O`6pf>^kCf5j%S;_thMy8?}HRz7}X+j`cpt3+60`2Aw-gbD`;i)61TtIi< z2VOcPFm)X%R7lZWIJ+W1K-82Bs3E2?Vhyw&%+_ULO&-U%Mwks))HSp9$o6(P^lRP( z0rt1&u@gyQSb6Z|$;sxH&B-Ru5@nv#DZj`a^{a5Nc9ZVIPyW@PdvbBhORp9DPyF4? zmw!{of)21Ygg^LR9Xbsau(x{;w z5M#}#jVu;{_rE^?#chAX1o+5xpQlq6W%-n+cIl>^-E&5sNxc+sFHBgaa!GcjTT63q zy4NE1f8+nkJoAm4gMaG@_#@wA+;;MK(2f`RuW;-&NCE{8xqme;tlbawh!`Rv7&Vay zOfOv_%{p@1lG_A1qkI@#j*j}3=y$Fof@p5LurgbVDu5yd#0Y63v$bGW2oM;nvLV(s z%$tgn%gADJeGdSykpN|!JY+m_-WZL{|=A`+8ZqbsvM8DwWhzjUaIM8*YU#ouUC4;R^h{HiBRt^y>Brq5PW5fj4 znq(8CQBe`0Auem?>yha+@WAUNKrb&!77+It>+8n&a_BbKf;Xe!aS*Uj%#zeWa#=7p zx#L-QKA^NaQZJe-i;(Le|iMuC5rhk+u!JF?d7`+|&&`lK21Mp@p8KHZGY}qn&Zp z1*+PuO3SvwVlY>b%gjq6Cm9avDi(Afy72J(PKD{(EBg7D#V7trUO5m)fHB}l-)-D= z_88D^2HcVTuTSsaUttEh%lL8F`9DjZ2_u2C4OK%34+_iEsT*5Em(uK4U0j=>78&$ z4ea0bSPAe-3q6RS3_f_bao3qw0PgjU>eoR6SrI>4js9kxC(i$o;`UiA9zY0eJ@*33 z-6`$vjBYk3w+VG63b;S254Y=$!sG0L{&QSkGs-m4zFQcI6c}P*Okjj*42%Q=LaeFU zYF`apX9wIsfa6yD-2HT)`n@)-o4q_}t9BT2JxabD2ahB3ILe7pFdJB=!Br~|F$I*% zbg;|*n{z++-ZgvWG_GeqFJ5}?Isp3DcU>^vd+`kdZT*2aLxKRMer!#SL!LVKhv)`p zsjOK3wFQFU(l?%Bxi@7wozg8ka@V3&^w0Bwzc0Ve^5^FEKp!8d&Yd!}dK&dofCd;K zhLXlXj0g-N4{8g|z8Xl$c;JDcmeIc$oa5;Wf0!65P>B+nVB?u**}HO$`SvdD-h$LF z@h;=J7ykxj&9L<|(kr>Y>X#y+rJ}7?{k1y9s){%mguHB5ilYK*VFQC9gdu1M3?&;_ zNHvvJEK}r+0vj6v@YNcEBLpaqcS!7ycf(%XIauYpW<$?DGHjGAj?FbfX=E0M)}2egIG5|rJ&pt;gZBiu z*mr)S1ocx7zJS_FSJnoT{+vC*>;NCkPzboycs=GDZ~IfIK1uYI-Zt8H$+N%p81wC2 z<~w_|^BH;B;bnnnF#v-d=>EffaO^&Vu>S!4;56c?b<3BplsvTF76?!aA0UR94JC#K z1EtWQSm7bE90c<6h#jy`09$q2aVvi8(25_~7{VA!V=`m6I5hL2n++^`<9J`7fky5% zsKPz|g*>;O`CPec}M{W4`}}0Mx)r*DeH| zAqzMaxf=4^*$?x=*$)$CxEXctS%!1%~{in<#YG!uW@atdiJk5vq3BZCJbd{+#?C?z^b^ChXIJf$~=Mf_5jvkG?+IEu_85*NeaadIOyfjzw3SdhmFTK z!o}?z+i9qV*w>Tbl);Y#IDP1XBcuDM4YC4y^?pBK38ik7`lOH0 zus67!FWmD{6b49vpp`i;zW%x2V)x25cCKwP+u5a^FG;CGxnJ70AJn;y1RN9jI|TTC zuxNi@IX4Ujt6v;2BB)ZCCn&3oVE{gmSxwFrb&f1kr0c@r$)N)Y@Zf{Md1HJ&v}+zy zYgMP*RK~zk9+@-FVU?gcM_ko;7@+LU7IW-~%C(0Fn@7iN`yb}1+tK!OK zZUFjS7y8)#Yqi>k20s)E9S?6e`R?O@?{hckQYjvqa-hu(vv7tl-1|S%*5`Tyu4MBD zGoJbKS9tDgPq4jlmFeati|LfKT;i!iInx)PJRaPe?K?I=wE7HTJhZ+qdRxF^k#{Rq zV2!XEvzibpQjX+N;GA0@55UaqzzBT!VdH+n#rY`A&UzfqmUWPtHpO9WK1#;eLKs<2 zV^n}Qr6Ho0s6$!eUCOcd%d^#&P5HjzGdIr$5Y55~&C6ZGkSJB0B#ws3WHvEDrMO8WQNuN+icUjh1_TT9B;O-K9n`|k^I=9R%e ztlvGaqSGac-6hd=uvi+)F0gC^^RDJ5up;WoRzt3?-TlY^+oOOU z)v?Eoz{9Ouz>BNE&mnN%asCy+zJCA6inV{-)&sxNy*`C!pC5IPzOX+3$6vZ_2lx0V z-u2>%?>@KFoL}qel#Sd4a<17u{X;zazQ0DU)^U|goe)dep>{dvH-Gt8*m(X$Heb5L z_QqA_JG(4rOLFRP^@~2#8#60&e&yD!M)=A#6xVqLhx(2((D$>5`aX_zZF#x0{<75^ zK?EvHMf{srM2n`?<`@*ra8vCNYf4%fflYaPTe(kUV(m!Y_aQL}yuc zTQjTw>5qTq!rHJ7*Y5eVY`yc(;vwQ5z-(V+ck>y){x3hqb5A_QwU;lkd3l5B_BPAC z87URfzl!826zN<7J$=0kLLmay5A+o&)RVya4(eUe!}AxSj~585VhLbL`;tIb)}!DS z14JoQ;8C%PW57!*7V;~p8o0+Tz%nXuD!g$?&cPY-Id{2 z(jnwx^&bRU%>b^Zepeyjl|iy{J5~=3WtuatT3KU*^-6nnT8IZWz)Au@zXp?tSwxB@ zg5{;H`;pE%7&oJl<&8~Kb-{d@f(@%cASfszBH1g6Xsa)fO1<4T0Y1QNS2v9EJ5S#P zY!1%ZZx`%_(SI0x&#>{^b^3{)|A80CDay0a^e3;_!PRdtE+g$VLuM6QT~VbvkEXN4Y!P=L1xiUb8gG{+e9|B&J` zc6SZ1qg{8)E$Er4F%G8G1k8h6fmA>@D}|hJmDLZ{%dj^HK7!fqQSj?n^7d~4`eyCo zQQf}}zIsD%3P1RdKJ+9?A=QY z0pE8_AJ|n>!=Q$`E{C(D^;7@Z`g`uXuPG|q`mt*Vm;g_H{OL%h%Yv zdX?>~SJ~a(Wx6+`OKquKr?QA|-!L!Zky7Zc5Av@jfm{bhU~lAU^}QjWx`IIi;|A)c z+%6@UcgP)E6{(Ez5cr3BXr&H<0^TpztLWFUPmFH6B=KBN005?QV+68LE-+h}v9`~! zoJ#RT-h3Ut+4N9P8>|oQ=3pP|0cd~6kWQMyoA{Qs1Q&UV9=oP5St94sf($wSYpYcE3>7g_(0@PPaJ-K zpi4?^U4yFpJKA;5(g6XUdB(V_@4#hi76*{CVZnmCIaaJkFHDj!dkgCD&1O0R=xa~s z?spE4N`S+o-?QP2tyufFIoY=)vi z)xfZU$p~K`V|9d-%cyU=-UBE$goViAJ3t9D^~+9Hri~Jn)R!K!z1?l5TU%^zZZX~2Wj>p+T(qRLTK>@c=GHF^JJo-{U;Oib z;Z{5O-cUw@?{}iCaCybx*=(-g`1r5##ZQ06W;?s=Zf`T)-e$VH!+dv-#e6~Ar2@Xn z^Ohnmp_F>T_J5PC7w!AcwCaGK7^{7&tdFRYu+lVWJb<+x{Izkp&IKW0<_Fg~XxY9Z zeEVYO(4_M9+sz5kw$LDQBIk?|DF3jzdiZSe*FXSX`S@f_Z0)R`msbz}UVm(1FgXM~ zL$1X?#HGofVHxgb?4fG2xg@Z-|4{wc+iW(|mp=3BeC}U=(zdT{GTqr_Z+nNm-CdS@ zdn{%%+O8$FiPY!x?I(8bOCdXWuEWoGlaKqtA%q^EGWr?TXflTN2|BR`qd}QSTK1yf z?MR=h>kfZ~VC9JP@}Kr4Y$p_(7hlKgz=H+`nbCL!P*mUO{4ObCX9u`6+$z zGr!JsdzabXF4LXTW_wc>vpK0<(6x!=3H52(2jGW8YHSt3>z|JUg4pVZ*r7h@RlRRPu9``(lNV)&MkLz18yR~|&L*N61ssKGK;B)c>oLon1qhc3ihrZT8UoV;cRYf_& zeo4Z`ugm{TfU7a9tY z^YIekHG+SG1h7(&VZAy;iv13^>s|v2k=^P?c|QD)B+j#n+Brs`fM5Qw12$fGhF|%G zkMrc?UtzH~Wx6+IKHX!!yJz$1jOAiMJ73auEuIom3Fo>Fde*}ABELMe?E`*2A$pO0 zs0>Wcf+8_uERnup1M3s)#5$ar5KgV(Yr_&oo-HsJ`&H5p9th=p(A!2|cWxyf#?H2}+~mjx6m_HOxoJo~=C4H&^zZdVE6_EqM&a(RQ#{L07q>KDIY)14_-ugx@D ziTUZ&meV<1*U~Lpa!w^}yI;EPy}DSf2RH)mA>faYbDtlF-mi?kRq)692wse6*pyOa zCni*9P7u$Yf>RTG(9lhnrK7T=luPOSRdNZGWRrtF@BdtbUQAd<$`GFA>cMbds<u-M-`M%2V& z=$)Jp&z@j#;S7T_CkZFkXa&q$QWD;EkWy(n$ZXXrVNfdxK))u_GFWmWnR&)6Tj-o1 zXSY`ISswuotAO2isp4rxKjU*>=BgQYww!h-wBSpEEy-G%a$~42|Ied%`tiT@ca&9; zQ-#z2A!CY2M)Gi5wLSdG_x|-iYR7<&U^FVF*j{|G^x?cc9{jQzEaBU!s2{R`sG#C=rCxkO68Js)I`uQ`goj!#P8g`d0 zNg{V$2>`VTvSX|Dg;;M4T3<$vmFUkv!brFhSVER@oZun^QB~rReoPe@9uylJdiuh| z<8<3YwUgswFU3l&*S2u6Q1*q$3uPAOzBE@Wsl%1}zenvQ!b*OYiTIZmlkWM&sqN#y zU$uAK8}>VVw(@Ka>3a*dzaCH3kjvlw*XY*nVLv5PhUZAvCO-4Yf5oRC`6V{5ZgAzw z6|P*q!nLbc*u1vI?#?!|>5O)huFz=!nxB7Z#~D!^Jh4J@&xC{L)Pnx z+MTd$u~~~P+9Cy#?~i`popZ3rpsc>66p~CpQQM}DV4Zm=5v;b#47Q!Y;1~it^boxN zv@_fAdfRjK*j2GwX0;1XX(luc^1Uebgr?@qB%hnKFno!7t4!xol6zhihXCnUH}AQ0 zGw=~KL%8F%(t($D`=ZK+M!zhS#Vyr{Yj^x7wCX)kE1riszpG2A5yD%9YDk z%f!w0HoMajyKmb}&Q5kOmDvk_dcVF0K7>B;;^4b&C#qfo#2|*leR&-EXA}=UDENrD@4R1Xa;|NWmaVsO*)4O^$;+M4Q+jz zt7kt%>xnjJmRWS3dE#r2@r|!M#@4l~Y+c*n(o5Ia+S*|@Td-Ut+BT88O!8hcu@dnm z+eiH(j{}_@4%$8;Zn7@z1{HAKwjxjr1^nRv)+TV~B=N!-h8NCo`utgLJ#m5ygCUa` zi0-AZ)NEczuxyJB;NE8%fp^`30jR!|tXTm*ummj)T2pNCnCN1ro;g>dSYI!qqh#*> z|J|M0uVnXe-#_&`=XBq`eYd{r%y4E%%A^E~lC>gG;2@D>`@s%iI1kiY{)xQASvf-N zz;AiaOOOBo0_edDg2;g5fPxXgu}#~N&5(95dc!_iPx}!7#l@gLZC;PuZDFbRX z8Z)Y(%;q!x>id7j>DdX#Cnrp&XH4^)@wj3>Z)m*YHCh$5sPWbUdj6c2A00UpF@4x+ z{v+T&;-D^nK_11wJ4&33W`EF+Y2KX;-2N8Xr5!dd?XkbT#pTr@J6VsuIkfR;QDS+C z6=lo@2Hy^tBz{l2bgxD8AG;kJoYg?3xC*Sov=P&WtnuWj((5TNzueiT^CMtk1zx;q zO#hqR-XyGq+-F%+*MU-<%rgtQc|RlQ)J!Laj8UCnIz_|Gp}Am%nO0&}qZ&j_Rn7kE z!!Nw^egcqY@b#}mZM$<8KT{JRi3y*O^m(xJ^VF&|Ay9cwqk$jX{642=C!Cy}a5g?; zI-OxnNeF?uG3v(f-r6i`Tf?F&1hwIrOBP^>oIfsvb+Y8eKYA3r3-~4Q6KTQkVe13f zUdJ75lV09ucx9KJ-4Rc%uXE7LSQ9}^Y08RvRuE|+ z>MBtxpu(&MtLr53WN9ExwRHd=;{$XO%S*;LQ6ApZyGa!WCCx$zDx0e?GqY(Ln4n?e z)K4rhkzg}Z%`IplAygJZBSB+I4b|Uy@ciL}YB+!IoB;gv3&QS}a5RPES$qB?m`m|) zz}etgY7Nvuse*ELcEX!K{t;*6GbU$aW_eCg)l@!qf-2VX{{_EP&DQQ{5s7*B3-`|S zOPupBu8l(C@0_Rl-vj-Huh70uFKWK64Pmqi2iqi<_gJ~I%g+8DSGP8~+FxNeNl6u| zs%Ac$(@bXQYzFf(I)I@|0UN=4ZA+00?N_KlDxd^PBt9gun&3Uv1qbl)0$?$aJr{z( zNmvNMtf}idl$q&V9WyhXnue*7OiaVn)MlnS7u8ag3Nx<|gX|E`-v4;_ORv8d__WUd z-}^@VkiUPY{jzhYOPH-bL#2*Jm0CTO25#SZhiN`xI-NxD%UH4v?fZ4yiLQvYcuR?0 zy(SXt@g1-o&`D>y+vn}~FJ}Ih!@XtVlsjki_A^-NW9zH1vknJ4xXb%wSN7Suw9lpO zEv^k$xtwOK;m{D676tS1jOKKTPD?23m;maUORK8Yep?oRMzDGb_(Ft2NGdF8(5#_f z22L|=9l$P$^zI2Ej}?Mq)Ux>F#Luka*6D+i3 zxwiW;$iIK*>$lFDe)Tx;yT|SU{PeT%=0lj2(hg-zI9qv|+K1@;wa38S+ix+Q<>d1S zepP##5S_hXT}@!Ag>GTm+q*lzFCu{M+;^Q8FFk*kv$zC4oC{1M(9e*;N+fpdY`~>$ z+~qxbPwlgDX`lVQZLY7caV_h!;}Q}x=5@_L(uBxxHG9PTFP;6Z z_6u65P_&kyYD{ag##8hHy`J*QEAMjzyq6H{-PF6q$gj?PlWDK6y;iC7WoUfqLS1PS zY6;ek!m_aFyJ}aiqUH!l?i2ejcR&C3dx76QzWy~wzXz}0wuPx4XIH7r(^wRO@pR0| z$r1DUoOxMNw22)HbSrsUr;mk62bPjM=fE!F{sqv>PGAf8vSYXu7A`d3B{9R7CeUAj z;Q-rQgWVBa-o;%xAiKWL+EbUvP$qteVhx&$uW#I~_AWIinfRu)H(> z=gp<+{dvVN#{P}QnI6}&2ShyrkQ_<*7;D5`?0s_~7F1G$k1zI5mP`M+3}wY#k=oS*fbm+y$hnn#X< z3weG4+|ui{F@7&?D|;*0<_7F-!Id4{QwOBi4j4Xl!0y2|Pj7DWnSP(^Nyb*pAv0?l zP9{^%j!r3$$An3a6*bcM*jPez{%n!SI8O-=0^d`r;m203<~{>7B;xkG0y zi4%7Yu(%{9e>&l~klKw*>*y4^*o^I3jn5KT$s(I&eGMCJA_v>>)IRR&WzwfFv3Bhe zdzW^3W^0q@2SYxS_Su(&z6yo+9OrW$o}4l{JfS){LyI}qRIT<$3*hHPC(mugk6Plu z{sPmvnBznW zRoGlZwm0Emhvdp0*_9aX?e9j-e}1sab7`N;CJYe@^_-Rk_fO9_dT_+_@Ra6kisltk zx3dKdLO!Qu^t5NcG+?y}B`^ocfm}p$EINUx*7%-huo5`|FJILD4uFrqoxc`Sy zFse!{^quu_s87LVnD)f9Cn`OQGXqjnOXJ!&b={}D>8f$Rc`%!C@9>1yB*;wBnW*Gq%q7(d>({JQp3_3PeED7G|H%cQ z`@mKPLXB9!WfGFcNX0C}EHje~u}oAmGfOcNOc&a}nH1@Fzw*03^ToB}jSm8!Rs)~C z2EXq;&c*d=L4|ofqX~h~di+74Tj4Ge$uT%$3*Mrr)4jz~>@IDTU`v9qa2gg*Sd??S z^FB*sh_|)^8|$#Wf$VJIF71$B+GXYP4x78%9BhwxdVQVG^!j`z$#{x{&DQA5yyqw{ zxqEoZ{kso1dvHW~a)uTaG|fW$bvk>T_E!ppU^y_un1P!DGsKEodL=8Np<3~*61o-h zk1mXOasfD*ic*MaE!OKfye5fBiiI>lCaUe6n&<~a?Z;u}Hq$Eo?w5b}OFy%AwE02c zN9J{WBBFS!L3=nef;oz+j3M7*QF8}$$JOs9r_dI?7eBEi1akhS3qgoNu=tdgwcf>i zKD{o!AHwDkcD8W)Bi#Nr{mXj{_qW;F+2ZnOlc!gQJe&4;I?1?X!UjTOFbjdBqU83; zDYx(4^~i9zb3XOLP4JaKYG)ci92?pJ>M%Re*xVDRI^ z+T+~fjL0j&^(GJrqaL(ia02!W1L_t*c zYm4JRTGPLHG0q8{?Q1Q0`8FIx4&U|$>~7-rwn-0mSveT7vA@gSXp^Tl*0{E^!ZWVV zRhP0Sj-kfs4RZ|~R~5I$XS{v)0rzg*V{-q9`gDR-6;_8u**8pQX3?-|1fK?2*8Zs= z6GJ9AW(4vkp#-g|`<^ljJoCz9!GCfA05}~BqXTR{5t2l#Z5#znP>rqRSX><0n*PnS zNPq83zxCxW4v#-P_}YT1hB#%txyzB;Yn?ifG>tqIV#pVhJFT7Xw6%RbA$IPPIF=Pb zTIbI?#txk?aGvDps(AD=Y|w}G0c@_p&IsAtB-!7kcd*THZ^USC#HEc5uCETc*6VXM zNx4MAHjY(|7N*p|Y1MFhJmJlI54rW$9gg0)&-~$W)c&gJ*uGe3Ff(ocsDR+HIMcLs z`li4{k%^#FF=irC;3x@A!&>PHyPfm**k#fm%?Fs5LfR9mN)Q!dw{Q%O)tsTO?HiP> zq)Na0rQiPQ7l+4NPp0{_&6J8*VAR~mcds9}a+#fj z+x6ovutpZ0J_`$yxmcy@w0>g7h&Cm zej9C+s+{j|;-$~68iwg?56yuDUu=qPQ$}vzp*TDxVZblAFz{aYaiDQg0 z5py$wo0EnLztS+#z%%c!{U1#LMw`ZiQ|PBg?M+hDv~3hY;-)Zb#a_)Y4C#jQ8q=IGX4io-D>pQCNeF9=$e#8nwCX8RbH*M6W{%8M36M)Zu-njYSk?m(p%G^Xyyr_tT zpdp|xU={>|nW~stHz4WPU;FA^3+Dd$Xa3^Z4+Vbf2hjQf34yOvf6VXoe-UvGx4yn` z0Hv$u(WYsg5$7be8NgOHt336SR{su;#Dn(p{*sbUw`e(cP*&<7eDp=r#}e% z<%csv2<&mlmF5lJPM$?b=&cPY@_bRswM1e@s3QVcn+b9t)^?v*?9pFD3rS4nTEB;^ z4X}+hY_tJ;o5=n)$=)`D{Vg{4wz;&q!PA2w&t!d`Nir@=!pMYy8L36zqQJ;aIc;hl z&ga}dIpghn4|wODJ3M&nHj~>A$d675<5>h>+dc8YiahM!_|cc{nMeNR^Z)1ihXEfmUO8H?&g!0i<5DsBdie^k53V8N z*xes;df%OwVO@eb1($+`XbHL(ZF?wy1=H6OhxJle9l+WE8x3K!1$*1b{uasph_&4v zwstnSvbN5%s{=lhu5jHY9FVeZaaD&I%uOj(8LQ{GEV)0+xqEWLo!j@hck3R9@7!f} z?~v+vOqdmD)v(we2KZTSj|X3Ji{aiG!l@W1>f}V!P6eBIbu*NLOe@x_hH7(>_4_c8 zK7jxTyl}%fy=xh0-DIH(l~2qX5vmZZ6q5pMPOyQgmI4{3nL#3^No*QXe&a_kJTMLN zub%&_Ywr&}{ORFF`Hz18OAlvFD!=;7$?%_k{rj7P>>nl*x5oD65%yPa6NfXoSjAmb z`dquFC3^SfjIL8RX7pl9whUIW&1mv%cN6wvEn6`sv{TuU-` z)EHRw(55J*8B^adZfXwa1rN?9+&etx!QBTO-MY`@?gQozkEze5*t|lknr=F#sN$zH zvm^-@!KVek)qLet;6#NJQBFlT6SE0P$O&nQuGZw<^UT%2>#u((?SC=>5a0%I?~2_8 z-5wqXH+4QSts9?}PIQi0u4XH!t_apMB@?yOR1;H8ELaj5LdL)Q;R}c6&Hd}oz53L9 zf`7DG{__9$^22GJAb5V~%~ARLceeen{@kB#*3bU^zOEgRWJ%ndleFYcVg*}8Tfd3?}yr9K$e4{uyuFyd%CSX^I+O)o8~H z^vZ=QUlK>KS-%%e{^lCCvjuxw*xnYlH-eo_(yeudYpd)J23&2m{(yuH92uZKvXZAw z#bjP^Hp>~GO&A}aF*!V8djFXD!z1eBGyFJ5^Aan|wzDTjAzGGbQ>%%46#PPv+{`8j zr@)aQ4;6bT!UI$vipjATPO)rCR?g}9lCazG>Z^;+(GR2j@B|jXEY6JM<_+WWUE|)B zkaFymruHEz5`5--ngz2ABoz&bwUaBYO$)bs1tVK1-ucZReD27C$xEOA!KI7ffA#*f za-hMX8Yf?=p%$n6yz|HZXa4NZ|Lj^mdP-}%o2Vpf zI~x)7y{P>*+CVnfVKb2GG5fKz=rcP zKWe@Z&AxGl$uYu_U`J~1NU&ouITgpa&cck+l_XT;x#!>@@bX(9+VT6y0?=9k`1TC| z<+vZtc51)T8&=j7F14O`KX(pX2(b_(YGT1eOvOya)SOum%%n|wikR}7H-GBrZl3wy zdgow1t%S%YjGY`7=d_rq8K$PD2qyRc=r5}4zw-0x`g7Op_|I<&Nz3o+!on`mpCm{x zYiIBbvC&4<{_S;aV;BcCWGSFnSrMeB!I(9HstF`zjVo((UZQzHn9g8Y5YAe_$1`j? zk0j2rg0ewEThs|{mOO-5**j0~KH~6^o6?^7b^;s;_E3>SL60Pn6A3nMG|ccx0aj6# z4VxFe{wLA?Cl&yp&7s~fzI{X4*#X{?39zn-G+{0p=c0-WBC1|67b}LU&a~y|XvY?B zX)#7>YWkmkeDxR&kx1GSNGy)(h!KP;q9!KdRMm=y$KmvcZ#3)AU(4Lh*W-d_*R^-G zala!DVpwTgvsPDNxDr9{r7EXiJGr+DAmX8DkKub38FrTpknQi zqjpP%i4x2#VB)cmNn{Z(m_uq7r;-OtFil%dVdSZ*ih(mVJNlzPZ?Y@9>EM|wG_Src z3)YSSrmbI)K$6B>LE18YgF@jE=1^6!7^KA5yl&zIuCm0>D`Xx8Al_RoFKVbtsQfwb zz5_nupwk=;K~2QYO}=@Mrakiuff(ltXPBG_a;#!UX6{JPi{NK6C$*A|vSGd+c=e;5 z`+p+=xF7^KZdm*yMsTHOI`^!H4DTBfL4pRHB&cs|@^OKlidrzU2D8Rg`xqH!5oF?? zAyG}tG-(~i_%uv?I0`Rs^ziiq)Gwu2A+s2J{c8}%-p|0aFhFH|Oc;2_* z8_W=iNgmZxadNC?$6)cyPh=VVoSqa&RWo1rTzx(8Iv?rW{~HUyk`Ta+8(a{A<6PEz zL%c5~1SRdt@*2#7wL~6mOsO1NV77u;Trg>LPNOrE1SK`o46KJ)53@)D#Bjl!I!Pq> zr1;U>K7aN~y7KImr1;@mu_Bm6U5hanao%p_At@tJMG>t6v?bVf9DDF_Aamo-1)&a+ zrd3A)kr2AU1Topv0bf>xqG*A4#yN-2BNoqll}d!UAu}+iR z>S1SMa;(CM;7(9E5%Fi@(&+H@k~z7RY<>LT!^c1fd5jQvhNsMYk9Ds`YJ^fPNnOxT z2UDpX6sF{ctYD#!k(mUwV|jr7TVS`Yx4;fQbUq(HrMGNzZ&|V zyPnj4@!EN*w_VR7K2H0@hebzFtP!+)@@{!xFvMCuexS|Qw>kUPQfO77vl7*?ID?T6 z_~q~}a`%brsUv;cB8>}^v7o0Ir|M#Q7iXv9bnF|Okm-zmQjk+pmCYvsK734!p!X7j zDLvLJ#rM$AXh?(e>a4O53KKVTs(nm)W+9VEACFu@!3DDnBNMX~)P`odZf3P%Dv@lG zpd^N+vF$8K^_Qg>~+nKkvwy`5Y@s(-68E zCa?fw`JC_QTMbxcL?VY+OW&4A<`9unHBQAPXQH9a?Yl{%HpQ|W?+O~LsLO`kUC-XD z`f-B~9~UKz_Yi{9Q>N>cZAf2z6*Q#km83RHi&UFF;xa=s9EoXIIDn`mqLN~vXKDk2 zt(jRVA$U;}i_t;chNYxt7CDGO`Mp;R)x->Oi6qkNi(BdAdYN?NV}bK7;1Cl$R(s5c zIEgC+vk+*VO|x^l@N&Xhc{lLU$!lA-B6F8HX0e=0OXE_`3_TO!4CPF46ETaqeV@%q z@)Fi6s;X&2z3zQJR`B6t6aaXP5R68iy0%FOx|L4VOXgLoTD|M3BQqaTATipIPb`l7 zQW6WAqV&vckcbud(3qNuwh;nxQDuBi7hzZ%Eoh7q2vjo()7erJz=hfQ^0ZOi7-cNx#+XihmQ3-@95j}7ZL_AT8V(MY zlRO_g`0z0c06azrUUzLF5=$CK-E$TiM~IUi&DDunlbUKz zY8j~b+NlMCieZTvnOn4+nTVO0SU~Y$8IhWvM8cr7%#e->*fj<4=pD{YyS2RnhAe5j zT+n#mJ{oBmyM|SU6u=zF!R44u1(_%|G2BGN#wgRM^*U=3ms42*X^FRrjk;kTJUcu3 zNrMlcfB?Xf5E#a{Ul9JXfX{wb8Qu!qUvpD3PjBX^*8~?F)UqyKAc$!Y%X%)?CS5U? zX{c+Q1y#c=Bo0eaQjJcav|WE*ltC9#nzce8OU)_mgdZX+v>W~Xcnc86u3iM6ZUn0> z4P~GxR3bhTza_srMBC*kb9 z*Y$}A04yboxMAG5p}hL4a99|hy{4R=2DTBVheEz5l%-7RK~)KLEo7yj{iLiMT(uadF;&wAFzIqr_|#Yz4Ev#hq9wu~R8&S%Og*pq&V@n2`3U1!x-x+GY$; zvA9Vd!y{4S4XME@usD;)C=ksBnIke&vl+-tuo;e-7&FvzNs_z?Qb4aHSVbLb)~klx z$9n!}UQxdD$!h)b^`ZFyANdUyeOSPa8}a$8ug3e~VZ48Kdj2z~r7*iLJbajtrYYX{ z2Cf=}z?%86sTM|{+YUD}wJm~fn%EjG8Z(*WQLPmROhJV;$+?kC5{rYDlQ}#G|cCo)z!e& ztIK4L|0m$XCujk>Yxy6sBn!6q@3)ayV{Wf2z{@X3DvxtUI;N-*>b~@PjksupRYe<= z(!_G7HbiU{W5tj@Xdfg4%W#sSmLcud)7UW(8&=(gqu_kSN;`QontT_>ubMU>5qP8) zr35P#ipT|Za}nl<7ASLZ%n4SAxS~lVHfwebn|>v>ZIwQ7`EqphS{m1*pV=pUec~42 zNw0;)c=1KY8*eyz4^ng`qoz--gPtTqv#@5!ngtsoGDL0F&=rgopzk22j)|@6);6s! zTLZ+BCGgvAVLXA73cJ{ zQ@OAT>(zwNu+qd4Ef;{_)4vUK|G4W@DFCn#0^!9M<$@5TX@(R%a6OP-Ph8LHriVz+ zL^DJ(g3C;_Gyf^l-kl|=q&Qq`UvDQ-c0&OqW`0B@nt#z&2+ZT8!K@Lp8gmV))nZmR zq%|5EOlx{sgZem?Yo+pWIKwo28i8MY&YyAt01&?UO~>J(BhMYfVMJ|P5$?{z zmBa_U)7rU!cY)N0ICZ=6)M+411N|~k^a5F?tW5*Qy*Q6=WyM&3#s0R>_z${%QUn0H zaIF)BvW)Wy%F?mjIKr+_RSxeRL+@zRp{qg&jumx;;0Wrl6``jsA$7Sbw0Kj)Ii(WA zRk6IArGYY0l0wOxlBY_VD*dr?n#Bp+7eT+E?eOUXzPmov0s!w40$5Jrz6czCQu3N1J~({eSppn>!a!fXe&Ph+ zao76@#rg9Wfx{)?c)zRNsIdezEP>2VFyN0~|6d5edtHwajK#M;#IHa0gZW7Z{L$t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H1AOJ~3K~#90?VZ1mWY?A7Kli>@RbAce8H&^ltu+M_0u_jX7X}l< z(a3h{aFWd&ICE||A{hCPaNs|H3v95N0ZHR%Hz5`dGy+zFAX@Yani07i*7VfRcdvtU z&b{ZouCDH$uKCriJ9zV6{pk92zUTLk8*pQRX zY=AH@Jl6Al@|hChnoEQD!d}_9-v4yM^_BQ{^f)H)n8=jCZ6c3|JfY!0&+pLi9Sutw zZqtxx`I43e4GS8+q$SaETc7it{*8Z)M?`Mxdpss`?B4%#0w3#V`;5Md$y6X;!}VeS zaF)ogTwe))R|)>F2^I`2%L~ul9{VS0Jwv=j-QhCyX5j6LkQPJ66{e0IL;Un z-?)CPBK~V4Ga^$W6Cz_J^oEAkof{$>BC(u9`L`x8*7sWhBPDz$lgP9r5{Z^XM`ELL z#m-X&!;-`?i8+ZCnLB!(lZ#5Y#_C{C0JtFWA1imy)Yu!V!Dj>?(a^c0A(nGg=@8sM zzq0$+DDgWI4P80gv35Wh)3GA4P#~O;ZW0Lg0Dub+-weGYB6FI;^-CHiG|XrjD|r$v zEe(l=M$bth&=DCE=+t&G5M(7vql7a6$X|4Hdfw2feCb%xamy1Xl#W1&M#`ME;tFsdD{T2|m(uaxPt_EdXc+ATSBgDFB)+-0unB(Gl2`BkHkM zK&6p_oqGPVoU`D#`Ah-emEfbM$d^zAnG~L z==*nw6o@1;iKq&oWkaH)V*_j$v1Y>tSQGSYU|1;_OeIVe2-h40&*=r6LwvLJJ@HpG zL>hh1mL)A?)%>_4Gooe0MnTZ&^C=O5sE6<~K-8FHAOQ)1gb+R`@mtm^Rn|ZQtU+IE zNz~`bYp&=3nCf>t1io}00$dyK;5j6~1&IF>we%MneVfwoEiDroJh#J$5hFJ0!w1H6 zjA(p%#BGJ50q;;%X17c5*y4a1t5@B z0$A1|D*!SbDEH?=;1!V-C;%eMa&3UHLkz_Y?ez!2B>?ao#P4W1piwa{;vMC9BYtFp z0U$CV>FuCEh^h)=i4K|?km#bU8UYA&G;ByJ`V(tnA+iuYu`B?P5Rg3HDiDGKp<%;< zj%R8dUPBOE5CE4X{#wJj2Q-{8;y}Z?YsTuxjj;?s;((xp7cfl7dYBZD@UKV^G5rJp z$=Lwn3Q8s*764T44=f2R!2uw%B(fkoAS70dSP@vWV8apX>kNVm0N^=^pOs(-w5%92 zE;@dq;@&v^fXo3c6S7`sjTD5U4kS%v00=}fv8MupdH@E1T&(<6A*_I4L14i~uOka3 z{!$|o0)!Q7#;jPVmk`)oM-ZF?fGZL|s1A=#RLiLB~i!J4V6grKp4 zlj{M3?Evs;-Lrx`hs09`pC|r->ii+`BL{4lDgdTPCSO1x*8%{O0NL3A-V4a|Q`ueu z2nfuzs}i6BV6Kv2jsRdzVnM6t8w7&W0JsS8Ey6dn_D_`Shs3X9zY^YvkGl>|0)%1+ z8u6WDAcPV;z6=Q9wJ$2EdNd4-K9J^&drH)h<^wik~!=F0{ZsARhKt3>+1^$*>ZFo(kL+P^G7Ch>b9;KbHIps ziEp&5XgJbRv#-@S7G|kc$J@%wUV~L7z%J2$n_A2&T-z~G>)=2qbFdI{)EK>s( zUl4*zMB$o(pzlg~(QkTxO0D5Dz3+=Rd~CnQ^G$@Cem_)KuM)q$x&?nK2q+R@A2vXJ zEqU#Oo;tv(Bl~>Oea_63#!0Fdp$-V)RaG0eJMfH3<_SQR5P`}v`<4W+$S{mO-4A{4 zZ&mc0-e0;huCa2zg?LA5{KbLpKkG;?=tDV#Q!V%t_5grTj8rdGeBPli5xjvIokZ}l zi+xA}Yms}!eFq`u@&Q8OerwmVV9$}JbQ3MsfMGvC!vSj*let*=+bwKV6_`~Xm>l0b z`nS72;NSo7gPmXCx8HoT^YOnrneBXh_Vbg!!y2-1eBTCR{sq#<) zMsHZKNCeuD`vDckf%gcdW?yP`q^ifbAI7w*0?ci)*uJTN;KIH2Fs^^KOO#yw%xAfZ z=^RP0QRDS8lHkFsmjohWx9R?|>i(qSUjisC!5M$GjB8Q|D#@N2@=b#R6k|@lbXD2=V_{YWPh{VP~KC zNsYHgE$rl^0E1(DqUY7Fz_@4R!K(v-46d+Gp#Q&EH?ie2q&MCNeOkx;9g!C4&}r2c zFbU9UBW65E-mpr`?-$K7A2!jSgT4v2Ul1go z&n93PHfHaJp?vuVYW@Ehc%q_zj`ZWtiL^*xp7rTIqtP|Em6fhdP#JS;W79z_NdB$*qsnEi|28^?VKKzS&ER zR=eH~f??QrgtViy^d-Yu&l55yhy-Yruv0Q$12gEgpq&*q-qRAC-oxwvDg`7Z8ferT zX!O>GmKJN+v~n%q@l!$Z=C8?x)WJ{!NS4vM73$+-z4rj<$UJqgXT6Az*bV}}_9O)a z0AC>KnLjxFZ@Ka{v3LUs>DSF#oQX<jj497hTcm>3)EV(>y`2%?TDiFf0LX(t(Jl~}L1@d`)|2vj#HyDo2<+bh z(vC7g<`C3mdS%Sd%e(Gv^+og_349;;M!kZ$+5+}(qb^~@hBQPax(UEWzjpN+-u%_} zT*O_$qI$m<@bZ7-h;O}&!2!UATXlu_nfH+H1*qd~EphazzU~#JKwDfuTi>b#I8nRc zM7e)KRLvgLE08=v>tY4D#0=y*_I{c-b@k%}b`6!-flMVqE{E6#M({*b0$UG*MH0p& zeYPAek(o+~TV#F>+|_s6W)NQa%Y6d;#@+iAshnFP;hjV%0n;_snH!6zkR8!$LG|hG zoh|FUeExZU{#SqXuXaBE|Ni)R=i`6C* znZyEdqGiHSRRwD-ry7bB*yECq_>jOygGY`Kw`7HtP)XxNqt{7~+NXck_4Qts1FX@Vf!A=BffFh$OJ! zuepegUK_E4-tP&YZvX%!2U5kpae7qMkd#$S#M=NXbTV0|IoGVCZ8B1FJ4Ys`o+!M= zNIUBTLh%S}UBM3M8-afT{9ku_Nu9&9Q4lo1hTX)iz_XPf8N9u$=U#)-u;A1ks8EEViL_Uliu`RysgBe5O2Zo@y?n~-^>IOMBtyBPj65qzu z3K?Yxl}HSkVp0cLN))xDVMHB@*{HnuG4K%-WAKWs1(|WbPymUI*y4vB{D+lvL7PByL(OwJf|(ta!(BlTW^#31~^7C zQzLb24oLbHg!p#OgD6(U&+EMj5@7cd+du|Z2G;nIH&RL`yB#!G2h&<@fekh$f3C-Z zSd2k9Ed`##9qb!{?~(ZveF-p}!ymC}+yHTr9N{RRRpB zq`^p3Kp+5kptpXf=i37ibWQ?Dl*$G%cgR3SKbUc? z#J8c#H+%J~rTVp#!^7I{S_cBz*{IqAD;l@$jvwB&dmhmlAc!TPFemfA+Mu@C;so1m ztM0!uQo`FX#{N7$8Oa{ph}E0EtV}K!&X>dYSbAg73_`CeMhb@U00avfo!TZq7=nQF zL10zE;tKv6aRWOf2i;-&?$xZ7MYCj_`dXdCb>aBT3fCtv`}xVws8hNQs6!e?1lr?! z+}ZW}-T%ug{P?fOiq!JBI$J4eXVvL8yETnQTg;$dAw?sJ#JIpo=(GTiu{{)rahHC( z%N7T*hJrsK^N`FTneU1ekYG+Mp$QR28x=_C4jf5m@mh0;9T8tBYNcZmN3`_u^QRefCdCAE^F@NHlGN7q~Fhgn&o zC@^ND2gvY=y0vawHpQMI6J6?lO?3bT^lbvC3i|0_vjYyueCeb>tHcj#-PgVsna9sS zzu6Z~%K7~w(z@y6&8Qft;$I^AmG~y|gNBD=F|H-3X5n=!F0tgArJIb8S9;#oSn`0= zCFM>Pw5MXo4;81!$W*@-vUS>&u57IqocOn$y&7sWNN``c&gb*Q4?Q4U?`74}zF(pa z*pR&l*2Rs6PNS>>0Bh4p)J&}JHC*VstPt<*5n>aZ+g&88pzS^34w3^Hs}280GDj)_ zq(jJx8D#6VOn-Mz&-syl$#^(L{F|}rX!{n4kdBnlV<-}JQ}lfev*%K3maz$@S!*&; z8wwq!E6<%H(~D-nN7RyXUqv)X9JP99MaLKF#I2kZc&s1lfDP6yhyy`X^Uzwz8x{F7 znOi4j8W%XRKYh$ zKIaapS4A|aCA1+?VkwFgkQ&ydN&=Gt9i497ZksXGGVqN`g6wpCe`~i|G+O8L-V6YE z6;Q>$&!N~}ho1Anew|Dux(9*D(u^eSl1huIzS9KDE7tC!Vfr|GBd{+8WR~3_GA(3I z;1Jn|Q+(4vzX8OLUyqK+r;kCWfX?aj<6-@j(DwMJ?98{HxBkP2yJJz`yS=-C`JW!| z?pwIPG61w{)R_@zUX^84Yc;X=+U=LKe5)1XfB~Qu;7pMI0t@6>%lR;U+*1LDvY`|J zsRlYBX<2w+N~c@D%j|$y?9<2^+lZlx{cS+-U#s@jRb7|eeb=L!z_-u|2&{g>DyWQ* zwN)%&lSaI?57h%m$ZqNOH$G4_R{(4j1doZ_rsJ3}lme|35CroTPul`ZwFMTK6C3GnGZ>Pr-J%1LH*52*cQnT8o_OISL1fS$8VpgnL0?ptljHX zQ<#-C3f*|V`tyZl3DE77Ad;ru2S(5^|+f~t3*SIo>8GZKKP^5=>5R#c~ zN(51=IYtTst0!kYGe5{^{-4{Ivl2LP3=%zb|6VEUrsrc7yERa6~A*g?}LME#lUzEil+NF6Xj?h*t$? zfxtqI9ia?845|*+oDBlX7t8Lt-$AQIrL&)({EYq{LJ}jWGl*?oBwpBiRiNjCx1(SM|9zQD<`+25o;*fXjHaE`U)jB+ixs+g2V*w zk=s-6cu^1>B01;(L$K(R*N^ku%)h)lnNDC&2O zMdusoWw_@^<1;qNXQg^G5?CU$N&sdGf`LLjXFk9s zL15!g4Fp-w0SPNx9h(_^9a8sdwa0bm@5=c#U1|o8luk*i%10h-dwJ`xyDoDIh0qb( zq*K0EN6ScwZB1kiHp#7y_gm=sZG=H(7%18fxWxuIwXOgH)BV&;`@eKfxzzIQ%MrD0WnGJctALuzi zutscxBjxmf#lM87Guah8is+9$3DD>MRETdw75YG^`w$o~UYGp{uC^@OnjG?Zs6`ua(SDeA(vu>jvD)FR0XW4Gj*)?(7QaralaA2 zj-odptjc{J4Ap3Q)0a=C^#^e!)$ca9$*(c}Y2~+peP2eMUU^E)A$ev=RL3I5RsPz!4#P@V9A~u=&iB;=&XMkmQ?!=eB()< z=zha$kr0uUK3*k(50+T)C_5nZ3s~*CPDB^`#7ets&Jui_|HUo8F~x92r!a?WrT3c* zd;+{f=J()Rah_90UH}Bo2)qjl0(-a2x5zFT_HSIHen6*nM`8mA5CjB~6`dQZQ6VX#J4y?GmsiJnbXhRzt|Pi5SoNXY}i8d9n~ke!6cZU{qP z3e-J|YO9tHEBly2+xww=UmtiGFxWP8Fu30>BQ@D|Cb@i8tJ+NDGVsgR!+Lo|EBOK- zu-w5%WWLc4Vo|_3wyjj+8P@+MhG5HvH8MHN>gYWXjQEXq&KU@jvlE7qGcO5}0%F*% zWt}^cKda3dRhZTv^JUa5)wcY4SW&69>U$up6#xB?jxiI2~WS$|V;=kRCpL{_O zEC?k=U}Z|3inTeZNP`g_b9D)&XtKp3fvxKHsOQ9*uB4s>5W#VO!Zy0LPMi_GM#fL? z2t_)W3)$&)<^Exv;)}Ncq~;oI0EbN*6A3AOngRx^4r+>F?Nx%L^hcbsBlhGcUq~HD zjrwCK^YCTUgjj%(G(g;9V^lYaV*!EeRn(UVLPU`Q-cHEWF)X!FbySEv9NSW*g(P3< zoIhWo)GymgFF^ui2LJ;>!bZ;38NUDxu13me8L1Hd2DnGIUeNsoN_;KBHV}M* zw4i^BG>fmu98q`2Xo*bK4gedc-kbu0NdyCe_0l)09%4}mwddY7eDQaA6Zu>kS*iZ$VwPTW?am234l%ZYV-9{n4FRp|NZeQr?8(em* z^ZfkTFHRm|a|Ep?E3hEz5wz<%>SMTT%a7FA!=er(Q;C7cmF@g_gvUcqR~5G>jb(Y?xA=XD$&~_CB#k`={t_`-MQ86szqz3JDT^| z*<8o#7;%HnSjb9vKWicAyafY8$6BSJ5Z~;84Ki;2gz6gp4&nX>Y^e&~=Nf_l&Xxu< zGItdKV=Po7=FTo4Y#b0|e8mPUjann!iqK-4T*_l65zL(&6@!W*`uiP>^_Jfb#~)W) zHc!bo-3DwJ03r)g0m1?tDEzRE5oMh!*Xm?nv~F#0!(CCX4uGUXwX6?I zGWZL1oW%~1qV7cx3>_;*Dh*)GMv1>cCXKI19BXXFM*FVFS`1o_7n?4%xwTMA9wGn$ z2*^o9K~!f+1N(_>Uh|CS2ZGqTHq2HsiV>SzAfwhOE@Z7RO4LCNRp?8+t#;`3lR^6; zK2SuuiGAU6nGP&jl(`y%3w^ADfPpb++`m=;tVsMb;$_LMq`8QH-FxWeTe^0^g>cj*fEpPaJ(vzGn8Bmh8P8ps!ybr~SsA@CKlCx-_D1dfr3 z+Ni*sXevp}KLJ7wGYgj(;bk1IeIg6+7}wVfVGaHSYEQ#e*BlB*FUbl3Ii3+501^ut z7Iei9SoJ`#rtU-JxqnIK12T`uMeXYE{_-nk&I1C}WW_ch+*OHSK$wwvAE}&M6%Lh{ zf*6Q3wQHL)r4lI<6VEbdbze`brR`VZ)bVzue|PrtlmAW4C)C;j-~U*fLARB=`7wJ{ z0?58?0)k5XIct`T)CMr(2NnM^u~h)@Ol^Q$Y6#BBL+*!HSu^M7+V3(JL4aUwYC|9_ z$vn~P5lQD4NDlv)z#J*48qx8Tw)D}Pe;lyPS>^nw=T0R+t;GwDuvW?3QGFLb6Um+j zelLYu4ldoX~MV$Be{X<^E4p3i%lj;d6`s4+ir8!d`>iJ<`?V+US(}_A-Ba5Y|Y3j@+IJ5K(Nf?49Sgm zYT8+qp@d>YdYJyN^?*QAnJwEDstM#|-H%+0hszF6(K7vrx+pn;)L z5Y*dZ_{9<~UaPyip`~L($CAW}ivA(-o$g1+#lF4(uyqk2fJ7H1%_-dHa}Dnyey|m# zn$|r~!FoWgxNQpu_X$}$m{T$0YLQ7TGBr=Z5=V7ff(?T*?iT`fF77XpKZ^#6wQu(& zHT>Fv#G9JTDw$(dlnCjV?tD$AaTH`T$|Xp!>Wj^(%4T+vsJf}H2~B=R5b)e8d%J~f8F#FLGm{X zHXN$XAJZ{a<8P?<#qc{N`pXaX#sR?AHXuOXPPlUh2yDZJPK~#T64LAe1Ar8k*v_A0 zQrYpdW)vmIAYD49D9i?^9npRRY1>-IwvdyNyEb$?I%4Mm$B$XD(!Vp#Ka2P~^nUMW zd}9G%>nw=?@G-XCdf=fNh8_fmG;ACQ7Mdcq3Eg7_gK>SG>TNj%-)Ar!j#-Ca_=;X{ z2==9bxrFON_;v3w0gLJUixU5o=)Z0N@M{=c`i!s@1W#y~5jj>>a6rQWEzew`$c8Z^ z1;mCit$WP&_ObB`mV+?vHwlobBVX%wd$#FDr#bv}IoF6@CHO@B`~%jsY*^Cmi}-Ne z08p0O8pVU)3!>X&M`rBLX_%@cFqJSY8S78tsHNix-&rKxxry47=iA|Iy+jB9KO7TX}q?8qXT2jXj$?SF9X2YMbs%Z)HUXl3lCb)smyS7b0 z9k#E%jZ8hW?mlZ|wZ!leNjO;&sbRy4mI*R`#&QMLo8NIdO1tDS0p}MX9)m(DPUn4KL!^h z_GV4gCrFj@f`+l)FIlw?+s=Khrr*X9KO#-+L-8*&5yXkJxd@QTRPPD>8veQubH@pi z#e1f@-ZFcK>LBic_;B3;0R4W2p(+4T2UenfjHI4r(wTG`9+H?7X_eniIQ|W`>tW!j0>FS^^+9t2Zy|}>BRvn-6xq6EO5Fm+x^;Zh zh7n!%WD|mWUH=JI&zH+?uRg-}9K?s0)86=ctxuI}?kgAFQ?8nmd7?UZfuvLCB)%ju z(;T~06quhb?OJw@tU01?nVa?Y%suafI_>NoJx=JDAw3Sa=(t74Z9V?MaXkZlzX#&O zO^^Wa{6!`|MkWXRL8Zgj$gst42;4Gk)Jt=-;Hn7oHf?N}mv zHm=D068O;;aW9{Hu<;YmO?A8Fi;f8$Pqgah zsd8`2h9`ReRL^$*Q+@6S`kVRhuemZzZVOEv5FT zQK2^fe1HEu&%O6a?vs1pbIyCt`}w@@=S1mfsZf%$kOKffsiun3BV5t{9Z(X&xm(V9 zh;V^;KUC9)5RyZwuo)?RkN%a<<& zo!nf!ZLB@)1l_$HGIwQI0004~p%nFBW$mI2+UJRkc1|p6p>2# zRZq1wzU|@;fBcv^JR~(T4ZihrUa0w+DC&}Z>R&Jn_qo{hldDbZ7Q=w+u=wqe53lH; zaIoSMatk*&z$uEnXuVuy(@iusRsaczlWzpsyBVj`7q{oi2VV(t!#mr*$y@4apM$Q# za|Hbe*Bo&;T(%O+i5&12MCLjGFhKGO4w!v;5d|!Eh2OBzZhWyxGj~l4{PJ7RK4{O? zK*;>tkSxc>pTsZF<)RLgU;dx+G zzUtJ}l&zhe(yyTNotA=}GOE|VfCRBrLCdys!kvkWix%>39VOilsi~n!-*}-%~#cDrwspk z6U_#byH$Fnj-z<}B(9GJW!Az>bO7sAajd|^W56C1Yn`g)^L%tM?j3Q4Mqz1!vdYXLSg?g5gN0R{qh&{GlEINeG|Y$`hy9RNZd^t-EPqPeCz=VMXTGU2ZLKz`0ydg z#QIIP1;t)s_26PJV@1r7TlX`E6=9G~JUG;W0ciRhy!rh$u|lqt&qu4asW0hVUq9$M zM`Mc8{nUBs!HyW!6zqW#Dn``}N{bY=Rk;Qf;xWiVeGQZeVB(bVnKe#XoQdMB-sEo3 zf2M)+a2HRCwMx0vT6%kXtz2BnD(l^foTLitb42=ZyoL5495@Xk3@UWyTB03;0RR(+ zdT$z&0whlpm5FO9t+#Mk(_A<0*89!-KK?T5= z7XW>i`m-{JkqcHp2rzoKR)DNssk~o|&0t6*1CD~)Ej5#R zK}d2^1MW^vUl(R(O73M2D<^S10qmn36?uK$q`yP0K;d`W{MT^p1i!3TY0|yf|973A zza3Joj4DIDute?Bjs*hSqcL2sQUM*|>zNuGDK(m48=Hc)^Poq(_l!&R?Z1ng>#MI8 zTtU%={nkNRo;RtL750bmBw*R4eaUhZqmTs`@Nr>pJE(mx(%Oibf+P+By(2T4Dba_c z9YdmT12L!IqQB;#>Z78rluS4i_01*@lGA?v-?&VK9kd=fWE!@#&CaX+H|*OzeP3l5kIJp+5hY^ zsBp}2BF(RhNvr~!VW@sLdVr>UL;h?lUF^EZq+$x{J3}8!*NM;+QGEjSX5$9nHQnQJ zSL(1lgHi?FdhgiRMeDjWH-DfL;E&>Sa&rEG^vA_roh!KOr^M7tAHa>#*?2}vDbJ<& zz=bMin+T;gsDBv9?N?J+^Y*rC5+q$@AU4GF?mdzWXustvkb`9qb8;L>c{9+9T|5t3 zEllQKI6E5>lCOTp-8e>#4AmnyaTznwC>~FAuIIaP;|5qk(4qy?un@#5X^W67Cq#x% z_4S4J4h}x4c0pCsWi+~Bm^(_g%BVGv0AK{EKKcH4IhZEm_n#5^ay@S31~tja*{=Cq zTQGZe5W8_I$FS%P@Nv35^W)U_Kesz=nJt1>=^smE>Zo(PyTdEiqF&f`_+7(&{`_p4 z8lCj3Ljc9)lp!j{_~c>x!aIJ$%7KFeFF|4|`diAk^1hucyjrFV-22s-@`&zDTc19tqXePd`8#lA-`}ZrOU)~KmnsqRm|D`Oi zmLO%`$rOfV7SSz`yMKHKKFnXZmgdLSAt0trtLrVyG?AjGm{Ig=YwKf1ZtiH|&-d>i z6%+jzen)a-%YY4oGJ1W!l#jI6PpU0i9{@MFwQ??|o71RoM`u#*ft;L50Hp-6nnmTn!}sqlgMv1V(!t2;M9f|}sWe~Urotow|M?|_)zSf_yz z#n4pt^V09{lK{fX@!|B;&&1sPX<1oW&Xm-CV0#hP8JtF!Xbj-FwX)B~?U~qkV=l8! z)lz?7^^r(?Zb*!^zDihif6PxSRzRWOm!ChGxX6;km6j9|L<(FVV}vhc zkr`lNMHI(#1@(Hp(i7woA<{e7v)uaZv?vTh6EK168*5c-rjP!umGx5~vFWr2hCs1) zmekQ+#_-x0GL>IG9U8&xl9@T<+>mHL3&Tf2=@(QAOqA*@RAw9z&&7quo2A(=n4|S#(zRpOS0`#aZM57`SAe!%;2iBxk+U zq@pPG-Mj0kFJ9L#N{RsPD<>bHr<2b2sL`Xir+^P94PU-6MYj!)7=v^l8$%5RAG81B zl_ez@r>Sk@=H)dr@I(|s`JK*^ur!Ri&vn?Rg8F8*rPMr@x2tNZ8=-pSec6WPJiv3V zuX8dw_mEFD>45$EsW0SZO=S!h`AQ2qrdJ$2$MX9h>%cp zNOsh;=4zvIFt1EpC>8FpP)6rtMzWOyaH&Cq(-u&i0%8hEdxU01X!i+ z*eKrtOOU`qJYyLhv=PX=Xa@K&P8~%6U*h-fg=gM(XZv`{E@>tO5TM@UV(nZ)>pozS zg;=l7Og50*^Ph~X-dA&n;%mp97g<=nG)M93i-RG_*Jm%RDREzgO}DKpi7<5I=uCes zhR0!i;Fj{l<_oG@_>f0;K)~YKaOQt+acj^}=Uw&7K_Zn<*NO$eQOM1!MUH4M;kz3m=gJz{U%y z57>OsC`^9fi#wg7@&xaqkATX!JFd{WE-M1CbRskuQq_Jyp2o~>-b%iTZO6q00DxG} z8MuRVhV)whum_aT>~rkx$P}^S$L3UVqmNrnkD>Sxz#~MA4<^ZpRIZQyYEOFw#ow(5 zK^-B0o|$!}q#kd0sXeWG%NgGr<_1H^j=YfPCIgp9Z60LXWUzK;R z5YesTEYV+dk@X$nb|PZ(8Ko`X)7ddG=P4Av74d4hep>kcz3`_t2xRygbP3kz@m`1Q*GaCCP5 zDEZZCfG*-ad1wbdupNnfiu?%E&BGoT^(2d^FMboyf>f~8R`YUo49tQ7XYJqRDWR46 z%J(bMp4U#a$$>U2dD448USR?6;>}axmow0D;q_g$w*RM6N9-YcoW35c^${*{TB&ZZj0;nF1EYBEI_ zr`j7a_Tp80ue~8opE?NDGCd+LV7Vc@+$`{IM`5#)kQ5!5-0E#k~n4D8A(p7EHB4y z(_)(!Yc>&pFt&PZ`=|v04dqh%*tTa#8m4$lLDR+jwgdZUZTY~cgZ+1tZl*_rABQs1 ziEJL5qwZiFNjk6?ak7%w<_9RY!4{bpH|~H{74%ZU5DsEtkj3VV2v%%?=}8Y8PhK?% zxTMh`8!YTG=rW0K$WN=&L?Q|Fgmb9&4m{jsyGW$yJ@#YLz0EV6nZRK-S<0L{IePD{ zD`o(T!C`#rre4Z+$SJ!9Wns{gp-;h)AS?GWwt;{Sb)ef!GGXumWVh9L@-StxjCcx}UlIuB|aclxp$xwi8k>XPxrxE$N6ba3^6&6I?q! zzjSW3sPbu^|JHhlo$`-PFUIiDB}$>yb8`2dL!_UeeFpjM{{fJQCPH|JjlB`p21?f5 zO%0-spBawe0t&m4uIragals5v2ML1^Z}gttUg}<5=YMDW9t#$u1%1GLdU65=p2ACcD_95L3Sv_tE`{YS z8(8fj#;jcir%AeZA3A+7w!atDkikT(xO)_Ku8)HiD(uRs^YRy_$s#|x6g~r@Ft!jT zARkM^eS|{gr95jqgLoW~p*{@bDBu0tb*ZR-l0~qLNRkOALk0Y^ZNm^aOwm{6-4U0H zb@Qw_3b^JidB21BE@88osu{uLSd0lv^3b~vPM!jAW9nOf0QNc?mAS7pMK5PAwH={4*oO81OP=1ZPn@Byhn- ze>N3Q>EMW=sqaz|$dkj7TV4VI-p@trKfr5--w z1b)Pv-Wf`0^w?F*K;9!6@JFQ0BWyzzILD;l+akf15Q4LYEdCiQfC0I4ZIm3fIAZc; zom~;!4sjA`Vl-e(LUiM?{pV3#FaBhxT$7)7blho};rhD#o7F@;bzQkLY8uEnaYtLa zwuHtphV>HPe;3c)Cn6R9hj(rE`*9YicUS}2F2gM!d+MiQF4Hbyz7ml)m&6@jrN-g? zIml|pOq@J)k4=``x{v$ip2;45oM+ik%$5uh*YxjZn(%HcP7IX&_F1)!4uQ_W3VQg>&bd{6;up=oDc0x9To3 zZfjWiNm(7Ab~0LucV4We7pTaG;(rck861S57p;L;Tx-di;}#I%)YH?jc=eE_RHc*k+1!v0x}jWF(He{Gnfl{21U z?!k*Q13*7%NRZ>Cxk%l#~c2{Mzwbc6krjA7V6-b&&BGZB3KET1Ho+ zpM*Pw0{p!}X`Vra+_!>b*EIJdim(9kJdVHm8DMm1$bjevKM5T*&l_ehJgGt(w6XxP z<#*Fnd&1I&!2C;02Y0Q765Q6zC~Zv9;@G4M^lh}`f@cbDdW2S}6Lb{MP!60#AaU6&x031i zY*svH5;#)y?W=YLD;_BHPsY(I9MW(aESTm#w}DHG$pH5r0{lfOQ~%6USil#O$bOu_ z(6`Owu^5{k&)A@(Jof&LfByT}uBAT$yA_p4O&VKcgK`!u^mBpgg0ZEg|4vU-MYh}L zBQl^`Btq#;oXAZGh?<0`>E&L|tDML>htb_6prAGt$joL{N+ek%Ams{!vZjq(j(op{ zi6Kf^X`ISe^>wNIT&|OYky`KhNGb-8TlOrVV zrfU=JgN4%eqE%KB6~e@oWS9_JN7c8KW_Dk|j{GA zu#+H9tv0$phyDP%VI(WWYj6Xhnb;>%D^3jmy}R}CI`>!LF?CIHMw==s#oMK znZcWu^Q|#IDUM>W`BWCaW$^U7G`^?%?MfH$`9btLu z>k;|+z_UxaAM=7HI}#?kZ+shKg4*LJD)#UWfiNg|wENEl-C9wxE?Fo?Y3H-1k2tk%eQT(l-E~mPH>gr0K z@^vGuet|jC}skN7p2wu>D6JE`Y_nI?*lkKtJQpU^(1{C{q^KfXj_l+(1Rz4?b%| zajQH9BxkDR3%z31g32Gdr3<;%PQZlsrny<}oD!h;G!-VUNefpioBUX+&v)@I`1IYQY_SRaJ3(`Twnhf5 z7$C(dPjrV1?UItdbS#@Lk0e{-Qf!#+HfWAJGPuE7!SK1+yhp8e8S0M_M<6Fc0hRUj zzgtuuY>OMbpQ0NrrNt??&<&+}iwg{O(!s279`zCJ3^;15yHK;v-;@tB!EqrT| z&%(&8Cr0J1QExR07i&-)7L>I3+F>*l%O?LWzog-|51+@cry&DO&d=G!2>qQl$DOPJbYr>BX97UY?glO$@aZ(J3A zNcbE6ka#nMN+^%(_@t}NiEtq@rvCBbeWN?Q^q_*r57{h(c-+Wo2RQzntD zlBPxtl>jEY+OK~psn$|bd`5g_5O!2tR75pvQ-gQpWVvV%io_AlX!eIgbQhfzGC2@G zS{OZ89HdM8k2V>W0SJQ<)qo&SXyL4JfZz6%;0-&f(9?8gbO}(jE}DyZE_snXImyRX zBiH#2Cg89x!9uB*dho|A9>bQFZThPIA5+u^I`AlKI(y;7tH!VUdtD?*lF+&Q^$Q;^ z&S_80Q^eC6?-oNJ{3V@&5O|9#PpjGY`_-D}&IZI{GNNJhfx& zkJ+G@H%QYae~n%Cvjf(vENdIae03{{)MJ+_pJ}J3Haz|&@cf1^8f}bb039I}1gUkV zZdv-Hr$Kh~81iU*l+DYd`D4y%Z+__na>O%`Vgaus82zVtw{42^FijP%FWMJUWbIe@ z?@6wez?@VzRSJDT&FgI$9>|{QH@1S8)7RRJ3?>UxLPFUZ6+E+hzozk`re}qEtHu+@H zh8`@x0ZI63*%PVS_qL#LtfSP0>7Wmxn zrI(fK!n3^F|18&myPV>map|AQ1<=s9=Aaw1rR7}>E~V*sHIxx=uZas)sDeoagI1Pg zO^og%n#GG#yw~NK&l_tVUAnJ5JrE*c6t+f{q`7!^pR&g0TqvO_5(Ts85@dSF*pHQd zM0p>n;2TsN;1)r-C?I8|4EoJd3}ZvrGJO-t^G7?+Hl+UA7lpAZOo3~2h}A)FCVa*R z>dl+L@Ec$kl4->1thNz}u@%Apn1E`rqd|;)ZnECTebyC*eqHJ48|E6}JWo)}VywX2 zj_+-XM@>POXF9TAD+oYXK?#*;#+A$Cg?GXfaW!^d^x$gZx;Ug~`}8*b&#Kk?zPM=< zoBUan4AUQykk_5fE-p|R>S5*XyQG0LwF2JK_#4)at%*WHzdxw+x7rGxiWp9J`K=F@ zviL}gG?{V*W|ekoBjBI(+>}VWg^WVtdCYx|uMAwXL0oIE)!$o`SxZrf$jipfMv;j- z-#cl$D;^&BIPL*{Z>~MwbRpmgR|6e|mz}e-B3j$f!1n?eR3FGXCBzca;Hzc!>$`s{ zi)E2pF{(5W#SA;<pQIvy00Y8x6&vw>x zuaR$;Uu`JeEFeIZ#HI&0ZOu6S^;+EKq5sKzh3UysSrvKwbdVZ9@d6%pBFEPj-(k0x z(A+-T9bkI(wllTJc=W3yZ z!T+xsM8JtFnz$QJpUP0T8ykbt-s4{iiUnJ9oZ{K9Pp)a3e>(mA`>_}sO~@&!ql}p% z=Jt+n3(1R70bYNXlu)`{x^y*DQux5&1Uutg3=%%eka!o!1SBfWThE3h-;x#}U_i*8 zjgvKq0BT;JH3z@O-;rx4lp5S#S|WHJ=a_Kr%Rq8S7or|2K|FYGHrv**6F(9;TaHVD z%p`*22QR|Yng282&6#}If1V_Ecf6UIo2<>)vcY0pcImcE;eKa7dsv+S^(l*8&>4Hs zVL>pY8f5hZ^%-TGiOCljI&0gb{xoGwr*u;((X8c_h{L2Hut<0N+Tz4V^F1!H3UC}BiH5XH{0#ok^ef#Knu&Am3?WfB9oxeniAybg>` z{myfd5;K~WaYaF|7_$~xE)dCG=`+&Igje046$Ob+;$;rvS8(p)I)0Ef>hE-ArlyVU zH8l<`5YWky3VWyI+WKj0#P~zk%|m zMn;SE_4QsA@8<#Fk9hT$FN2wxnIsAsMNZ}QZ-GyrrnYBhmAvUYe${de3Jx zCO_3NsWyMMtC`IEEz5xKwjUQ=5Ls8ux9my%wBT4@-Bxes4IjdBKQyW$!kw%}vUk}s z%Z!HYoqrQ_s1ZL{U|)?~*41#iRp`V*qc zvtJhJEQT+_lt=xP^AUmp5`qZ32m^9L4c#a!SA|fPLivq+^DPe3Za)FD2VXt-{KNI< zY&n{xw!BbtVfs(m&abDi22c8U(ZSR}Gl17~ad zjNltc@p~}?f!t&gQ6;zfJ%ym5ObRR!?~P4OSzUtJT*%CfjT3J$3hoaMsuNPZi4~qI zl?lm6)|kh3>=*xaXG&!Fx@p-8h&5-OBP*keMvQK3rh5PN_I6rM#=`=GupkAa6Bu+Z zsbp!bJK(sAOH=W+D!4`&Sl69X3>0k6p~Hj9u6xu%gOU{Z?cPPJcMgdXoE=nMUmt$` zZ`+bn-mM>^4EVH zUV{lK2EEd}rJ`=HiG)VGa<%b%p=c*cgFn}c=L>s**U}580BNJ=s>KKbgvPIr;t91C z&p$IFfI|C%P>+sVG>%>F`g}X{zPyqD-`<89{1$Uv+?*$lt1vH+J->dq;66$E`YhJD ze^F-K_a>gXqqtJCd$vF&k}G8Iyd)JBoZ3(Eh3c-?VcyF#>xuWI{?Blv7h+|7W$)la+O;qczz=&q*L-Z0x2ch~Fq&?2F5yE| zM`gi)c`gVhChiS?Qs;Fnh!9kIO#wYf%xXf6^rRd0PK6L5I9qF%odWdNt`GOF+*F!; zV(;W(sP3${3|DSVhdxG;zdUOwF9M<%(PFLdw) zk$yHl3L<*=k1{lg4hB!WIy)mxDJ{MK6K9=6m7TH{kW7Ry3_U0XO)cccTprZZ&(;b& zeiQFL5023o8%g1w;Yn-ZY@+MW%>zRj$57~_^&7@j76lglFPNUy*#3SE!{2cEFg`I9 z8#MFL>bc6EaQk`R=D4A7?S!=I8c{q0{F6JZ5s3lwSJahHs#O=v-35WGLaY(|v z>I0iKx(*;)>wfukaCHus_v(W$u*tWlO`k2%pxfCX-!_4FhGE;EEaw|R_I-Qp7quyL zx7CC<*(_WrU%j7{)^DpfbAwl3T`)2*iby|*+9Uvq?W0J(fL;MTlgcO=3oKT6IesCa z?&xwaU|n{7X7q`mp2EY^q^msMbT8 z7p~f*$1fd@te~kW)8WE8^W&>DTqrjf<-xAx2JJC(2ytibPoX ziQK2hik@qS9WCuy0|smsW@e}YgGUu6x4izWP_R9_f~ux7U!PlE^ahgnTkR(K6ztJO zbBQ8r3D2M1zsrd#WujfrF+~0l?|bfCF|A}fj&Cv8-IRtB#*3Zq3pc@Bn*N%V@c5{b zQJguP3^dVcX5GOEufQUGmEtM9;S4=f60AT8Zy^gJ!^)<1jzzh$&ihFf_U_BK;O|yb%{xlyh|?lP`IS~;DiSjpW3;T}@!kM7S#zj2Z4fJ_CEcqF z?_5?}iMsh{2&jE;GO1q3GkoxkDxpSASQc=#{)Ncbtk93C5XW#Nb4)I^vESHwb*sPuU5jCI)QEM zP%rJ+qoH0g?`+JS0sAjtU#XH+^#aM7T%o^SYWs-qCPe<=sY5X^z*tsXA7s1g`oB`=r2mZb!SFYN#w>kdDCVz-qI5ZuQqrI8Y8LiCLw1ues}z;ui1 z?Tc^B_yOp3KOF9PtccdqYT!FSHJMAzrQXDsAaj%BStBBCuu%np0WU%YI4U3GU~f;5 zJv{-!oa;Z1i%*)kq;N&)`G)mAe3P`AKo#0C^m1B9J!;1M;T^~Z?HD>cT)1H`lLmiG zZ=p;Jq^Jb{rIy)J{@9xlJkufOG5UieUjpt5A4{E*@}d|9n3&V0zl*0Jphl>f<3QT} z8tXOO@#S-G@9{U*js&fXAK_DOdhEH0Xn0HiKg8SQxlBSdATz2_z{BELaZZ3@t@d@Z zUu(<9cYHZ3e5BnkgXE)^C6W1`_s)03HDmnZ)6cbNxR6P!TKRMRXc{~_oraFghYSx_ zuRja%>IE*r@UH+DicZXXq4g2B=J>$zrG&9%;Kbf>xGjM$a%Ii3x$Y$)zO;J0yEWL9*EywO4{eIG>e+4`@n6z*s)D*o)-QS#G7zt;eTA61>i z4OC;S6Ca~!j;TD8xWc&9mUyCT)eE213M7xorDbJdxzuc}twC-qDWh`df6{+QPLSTo4T_ z>+IE@AIf%)QB1kx$up8(CnTS)D{QLFn(`SL8Cy*o+$J%O`Ff=|_}GoUG+Kd!TBOwJ zbd+#yDM|p@w#>A}4g8aecJ=xJ9<%?d>lhx~~?}08i4OUE?=SF&qBq{jtfi z#?~*<*Z+nrL*wR!2gg|y3&Eqt_R?TET6KBX>?YRH_ zTJPeh1TVVdi$&d2zL1Z3Q3BFQ1^kB02f}Z)QG|ZyLwpr|{@KLJ9;c<1s<7X|cOURg zyCx8alfxLH=L2W^P*PrEcNt)Cluz5_O|YrdfZbwDRx@s)CFCkA^HOR^h45trlynxt@P^ITg|F+id?7BZk_^L6zzQ>^0Jl#FXdf(%yFRsSP z#ZO_d@$mUeZ6cTN_s+lm_n49*G@iOd+X^L0qo$dv7zc@8(d?T6ykK*dmXRVP-UJF9 zcYSf~m+%##`FAy2y7AxYyXG``Y_5D#j`>`Sd5~PfX5iYoysMNxFryt9x>}$zEq}RH z_Mqc*F^+eMbh#5ao|+OS#PAms&|N|vfRJBTs&;&EgXbQJamJ6i(8O}PLb@*H?$nna zL+vuM&9A5!aUm+W|244)9XZMU%ExWDJtuIpaB72!4bep~!}97ky9YtQ{EiYaP4>kz zR;wzqL^Jn!FK60 z<+DIe4!52<{^0$dWyw#y7=TYP{PnJX;6GE-fPK{?G7D+1mG1l6*;{nYuY`Yo^F+Ro zzI8F&y!XsCFwihD`ckh+vb&$3|Fd(PhTQNig~G;5{#Uq*i(<`4G9cb8cM4-Lj22gk<^4-?jjkqZUhfWxocO6ECZOBAb(fkFTKv-TY0K1QFb z5WL?dTn?1H&S7M+u?KKOl1QY+u6H7aLGE;`?Eb>_;W){va(+N0rHxL-D(0$84~n-L3G4_v-{= zfWv$q7GQu%G+{Y!Lq`p09PyhtgDgk~U3AMES?u2K1Nsd3j?J_#^A`FS+K$;we*P@^ zVmD`@3AfI-hy;7AF2{$^;QKL`)E>a;sdgq6D*()#LDomImkAVZwTw{W;v~rSQJ>yp zO?(h1Bprc(OI5DSX(#na&W_!PgSI+-%DtpcK>oZ}2<9UNWu+9~_IVRW(xps9DBs=5 z)O;NA{%$|jq9J=A#M6iJBB|5CtdcF|03YSpeSDd^cdpw;UNY%COW39H%Fb?8M?|i; zTY`AyaOQ~YYP@;x>i7KQ({_)E0j)xqmqz3JH_1sHs#eFGp{lw`g)#>!F=7KCZf7}4 zsd50If}M=G?#6{Jz06Lj$ZFwj!D8+#Lun@aMyM0 zSNrTW1v|l<+6bj{r|)%OU~x>bS`r*;B@dUmy~fRn3r7CekvX>k;2Z)H6Ox1p0BJJy)I;Y<$e8qp{lfDS|}pa?q(*g9l> z?&v#U9Ji{1uJFOH9enD)Oplm#eO@`sy*}v==I1;;eKz#z=BuEq@gfH}&_SfI=x32u zw29So?qd=w{^RyTtonBcN6Hqh6*sxQ&fsXI&*tYS$K>?;^;6rpmd*1Z{`-4!)Hu={ za{zdaCTLA32zu3x^D79N7n$*Fjo0&t3{!%vlmn>)1ih#NAM(-ZsTR$r@8YiaS>hap znQl7%@72se2NUdtRCY9G;?opmp?-4uqeA_0rY95J9_fS8CHlkaZ}1zO_HXN_%Ja<);0)qs zc$hDzsD%?ygn6XeDFwB~sPi4*N3*5x2Q4o+q)`=0pNOdKryY^y*#Fl7>uTT?6tK8O z`|aXFCfr)J-lRhHQG4L|KO@s`H8%ZRYBV5pP`eNzkpB4bgLZf{ckCX9;L$+enmN?30R%Xtl4 m+>lMn;l90ZxgoM~O&WaBaC?N^`a5B78la}6g{pjL75abkU$IsI literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/128x128/instances/skeleton_legacy.png b/launcher/resources/multimc/128x128/instances/skeleton_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..55fcf5a991a78c85f1491aaac2069ba4891374f3 GIT binary patch literal 3673 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 Y7@cK+bGdyu`Go*jnj%c9jC^kW526Yd#sB~S literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/brick_legacy.png b/launcher/resources/multimc/32x32/instances/brick_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..c324fda06304c72b6ca19d95bbfc610b60d22a4b GIT binary patch literal 2388 zcmV-a39I&rP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1b>a`QF}{LfSL2qeH4j>G3nZ_wj+`I2SF{*rWR zMG{3(O8~n78P)#zyQ{zOBQjoD7or#MHGWpCP(mZ>^^fT`kqEsD5F;Qc5<4U|O;B_XP5m&>4afSohmRZd%vn@eDp4>Zkt?vPP z3i4^G*XUoZm*7*{{rnP3zhi{P+ZV>Y5B?1CZDPJGqURUE=5A~;)zd2Oz zWrWD+c*P2K(NHeT7|e|;Mu8C1H*anR_#B&lebW!53I^Q_bA*FQ=LM0Sd1#9#&x~;n z_mx6Mg}MO{BFybz43ut-_J*DD5CcĆoaD8X`KOBoCp=cvL+LV|cwfg+8$@l7yu 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 aw*LV9JeZus+tdjF0000 zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Yuvg@c4{AU%t1Slv9mctm)J9x|QWRNV$KECHh zJS!|AKs996fNKByx2u2fBZ^u%mf)lJ8b23Zq=Z4x>lf$UXgBZkM|%nHw|R9g5T-1i zcD+j4`xEB*^}(UTeWch)0#JK3yN(Gd<=3mHE&bP-Z)L! z|I(I+W-A-->N?>BC{@NTOVli;aVNViu;fUCZ{BzUd|aD;eTg50Dj4JmbAW?M=OrSudBv8UJX`Ez zJXZ=CC3On`M9|GJ##k_*y|HY((LyA~0LK7=5-ca{D1!mROch2F62y}d3N&=%8!>Y) zF?kx(s7)Y(tw%luFqA-+bBy>A7Z5{mX`#iII%2Kc?mBnhrH3ASI$7JSzFmL8n%S(;WNPe_ zlQqmLkwb(T<%9=kV2qpr<1!dPK-1t%2jd(K<_2dv5|V-mM)KfBRWld^g{~RmZcgrA z%>9Zt#o<@H(Vv)8gS!6*a|+bG^Y($YI{I9hjIAsjm}-mcYn=k(D5TD=kA{M`ko4O# ziDTo~7kFmIP#alm#U-8Q5WCbGP^@DUXOz47-ROMxCztsVc7#Jmb7_uhDNeQQmv~)6 zt83h?>lw^W3mDzo9nu6YFZ-lqeOQ=49ZD@b)AC{Vxft{Of@5>9xg`%+#?x?NJ4eX= z*g2ikuxgsoO6)oaFL7uDMUvS|YxMgrng3>rG8p{0gJ5y+3`Pay{R7#!5q|)hx zzl4RB*b)zLtAg9nwS`%T2Mg4ex`57=)-alicwT-WZ#w&%`RXt1|7x~++P&ctHVwJ6 zObz6AG>J2%Jp*IfthUi-wDjeFL||(~_+ol06l9IA!g)4C~n;#(T*f1z=@e17fo(ijka@PC`85@HlpSI!S%Cxy*|lZa}x!Pc*EKr}pav*B>3t1Kt9t^N>(%DGdmlT%kZ4wq3d8?Suy zIIN_@l@yP0^RfHn{d)6)1N%of-%7~|&`J3PsF{oEnT!`A#*aGiSdeD^|>ovCEOy)^RhFcYm4C> z$T(D}-A!=Dzl6mV{$E2R|084ld5RI=Bjg;0K74qm!bGl=#1-&?3fz<9@um z_qclp_!||bnqA|7s#!)lnGmwMRiWnT{x)g6H_UhmWs!ah~OU?$6Pu zlb$Dz5(~u+ zmOGf043&6_IHsr?<@>WPE1b7DtCbpS-IKpCl-E|4xlVHkNi1RsA_T~&p^OSF#A($? zF_EVIgol5?@r&e=$yEj;#{#NQAvu2VKlt6PS(uz~lR^og`^C0DhJl`4pjo%=?_=9; zo&bSo;7V)zs|{f0lk|F9iyZ;sZQ$a%t;u`9hg8%>k32;bRa{vGi!vFvd!vV){sAK>D00(qQO+^Rf2on!0Gw~st#sB~TmPtfG zR9M5+mpx9yKoEt;act~Zu`q}xeHsc{l#ZT~GeFHbxB?Qn3k3)ynn?5%kOpY5OiU~* z6!AlviPt}(@MN0ode^J{X5YMZtkci=iYnqO=ELF8YBW!VeDCr3g@}lH)N%CDxZ2w# zBBDd`*@S8I!TQHmP`c1Vl(7E&B4z!{!9MGs6u){h0P>6PS2iJVheSkl-gnoet8&tL zY6~s&*x6sZOSTTF%YckJ@>%W_t+Z0@<8Z*WBG&0X12{9N9pp!LR_r({B~O+yJA1^) z%h@^z%qQPdt^ttCXi7f$Bg{&xWct3YhM#nyBm+<_9L}tEUL7Nf+4G4wTeCmVmiT}q z-%zv2$SkKyjhK8wj}PEBYfEk#H<6=Sc06CX2L4M9QfL8pf}F1>1E|};bxL%L<2i(>u{zs2l=K}>@9J^_TJ9bZgNJwJ^-R|Snchme@Y;e)g!eceMC*Uv734Esj0000< KMNUMnLSTY38>03A literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/dirt_legacy.png b/launcher/resources/multimc/32x32/instances/dirt_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..9e19eb8fa79ce150c2d1820499c68294eae8155f GIT binary patch literal 482 zcmV<80UiE{P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0003|NklcO`Q2WFLI7oLT7LA}tCG<{CmA0t zZ6g4dTAnJK^d+N%qR+ne2nqqn0qPc&@snCiZ%jpF1bch z@x22$0YeR3a=oD#W!7RTfX`JObswdj&LUUeK9k2R{sVxE9Xr6|7<0+1*k2VMAd`?i z05ULEC_Gzu;eI*J%cEh$uEefm^#ClgziOyI+2qRJgFnQs93Yzn8a^!hnB1&WP}2&4 YFZ+_B+4npg`Tzg`07*qoM6N<$f@&ejP5=M^ literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/enderpearl_legacy.png b/launcher/resources/multimc/32x32/instances/enderpearl_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..a818eb8e671a46e99523f269234f60dd5c4ff32d GIT binary patch literal 2120 zcmV-O2)Fl%P)T~`&xe{1i3UibROP8>g)n8tQen#dsnJG4cr(x9p$h!J82BUA|?!Gsx1 z;6Lb;2@@tj2bGX0q7tG+6)K>{Nkf}g+oW-0$F&{DkL&w5_nf^KgLCbs57BmldZewR ztGmznzV)qdowc^8Dxc*N5dnm=7@BiJw=3ki0FWd~nktQX#s3XH02Y?Nwlyr>xW-jFxUAM#HVh;$PM&y4I5D~+vv5cMiu#5eKs!ofV zX;ls(><6v&ceYVBM7XOGZi&dPvS$|=Xsz>~m5!>0&qY8)gqfL$Zg{DH4TKccsQ*BL(iLbbx(f1+qVvXZu`Jv>r0FJCt`wi7#>WVVh%h;s5Qjrnm_zMZ z&^d%7B4$XEIFckbHi<>j6fuTS^w3_Rp<9H|X%||SnF z5j>G2Ryl*pInY^zQCq8Jwow=22(6`%M5tG!+ryXysbwP0S)8@S`o?Inyt;A0oiaOhnNHAka{KR!vAJAob?#9q3AEZ>Kv zRissb6Yv2vA{stm+~B@izOXbATI~$Ttb&ij-_itk^TtlrxHk;6FqeY1p1`{g+sDi|f?9pl0TUr?@-d~$V zLdS~T^zC*_MDD4o2QYimpBhVD5{?3AL1rbg!?s>C{>f83_uJoa;rO>{Ew3V%-$LGe z2f2I$-djeSEkY+pJ6%Gaqj`ZA1==gnvLv1uGiIPR3^EIxbxAnNsj;+Q-g^ode0?|s zKP%{Igi+TRaA9nSXMgnzei9wx=S#2iub0nbUb~81eHT{mqrDQ0L8~wTP?1W7Dl!lu znVfK)Yj2Md^mI_J;p>Y)7w}F*go%mRmF1Tda~e3}2I`sAGRmKQpJ$>2{NU=#JpY$J zBmaB@xp@b+TWA?77-vy2do~IJCMd$L&jgc1#1D;Ty`_~Sf}D0`d8wS3SQC-11B5gk zP~V%v=uxqD&<@lk4A#)EKFW{&{sNnqt|6~nLT=tcTV3!aC@6}Qu2Ml2(ST7UC>UeF z7%&1xh(?B`SY02~qByFaDbn;UAa|U_Bx^e8b2AN$NE}C4gC2btxpIrGH{Qbh`%SpD zNZ8&%d_Y5hpr|TXUjg;y)+nm>lqHmX0#cDNwOSOmwnspwlSBvevurq;a}xAkLq!e) z`)m>$tU((o_8+gqjoZk3D};6zyjI|)N4-aV=tpRg$U&^2ih2)a2|eknP=Ym>G%@umY_%gnrJ0qCTKS2|l0#*Z=}j_JNJ; zDT1>QM`-LY^;Dq3(m+PHD~>!DBI8ia5OF5Us+To)Fstk6y)Cr#5c<(+Uxg5=NJS9U zO8#C!FoJ~Ydtj^qNztJ?G#VHm0O?~HN1i`m-`&dy1VLIo*lMAjZbhK~pBRHYkn@TZ zXz1s?@Rdn}Rz@92@iS9sEv~+lAB7`H6b&Up2ej0pKx{t>Rm55(jWM+plVwN}S1XPY zYrz<*QKd4Q?wStJ$ib0AxUW2d$qPbr2ha*jLrIdTBTZF0J010GB6LExH)v{^5LrkP z#4CgdyoX-#L4HF34SP|EH5Ku_(lf~D0Y=ZBW_Wy*MIYF1cNFNLY>J^nnyR5OuSQ%` zlqG@f(9MH5heQ!3jgeZ4NfIQE`YWy+A60uKO^_t1ep_3Svlbm0V*fWE<6B>!Mb|dy ztZxyzc@WqZWyy$Z(wJAnc|xAcmJ)6$x)$2)Laf;XG)ask5fZt*g|morRcg|tT1gDn zA#sd8bP##^F;0HxDdw^|;raq$yM^YxLeVuP+{$y=;yl3tO1HZsb@Q&O+|;6cq*%M3 zL?Z`e*GN@Db?+{vgVI;6q;Et*fLe-9Phie`jVHf%j%P;>@#>Z9th{!G^8Ti(4_hEN z#p=7Xx;ub6RaFt`l{3?KOyMsGa-z4q(y&8~n#69mt@K+>pMVb!5UQ#}hX&A-N2xvi zIOm>xf@cR0^4{hKFFyY#wicG~tyTwEQ1pU#?hcE$daA0s-7sKV>&B-SRpkUhNAufD z6WQ@;SJjxJyCg&~%3i%|5}^+r#6EtK@n@dkheuEFOptI#c{rf%Ap^PuBlr=9f{S4QH)V}=JcsM8QC9i&-T4Q5(8v58&{NbV0p8+`sGK-PXcziT-2lvZvBMNCk`0~Sq;RccynETs= zdmG5|J;Lfn@U2!CWfk-y=u2MgE9AQH(aHGpjpfb_nXDF>T4LDW5RN@uHJ`ip2rKKV y?QYO^yHM{x^H~1HJD7YSj%Ht&!|DIyc>Z5=Orr?GlSw210000vK~z|UotMjR8`l-ae`jXM;X|U{vLu$a7p@}#Dd5806j4wq z+icQ}*I5(_{0B5ZSEg4*+O7-4$iCfm(HgN)6qVx|h@04M>ISvlS}%usP^3svB01#D zo!iA7O0p>H(E|+by>suK^WE>9IrEKZtr0=y=5W^6Nw_X)-zVX^*g=5B5#~6AtrjX4 z!)vvukB#Box<#Nh`l$8CnShp=8SG4kr0X)Q6cd)ku-2qN*&tP{l7U*54PQdW%XiNroV6dfHpQ@yY9W4916Xr zGvChV?}Z|HwAQBv;oQ850JiUwa2$rS*w7702tTh0?;{ zU2o~amyjnAL}^Y3!UFI-Bo@N~l9rXiaYk^QVH{`V#0P8*{5`uiczdEU{O5DU_P++m zk>nPRAaW`OI-M{?M6fL@jpG;)v$K%Nym@o225UoiF4ua$p_N)33aPCTQlLedn=~XZ z>ZEWu2M1ashJh3Sz7L6?{(}BQpIKl5F@d;1494QZ<=EtP;!_ZZJ4=7z>#sh4H?HtT zq5WZ=P(+T+3Fq()%SydA?oafQ|KgX%dD=2pkC-+k&|^Vb%)M7WBX!0HHYok*86Mw4 z9s)!ZZvfE+;7Ai76*)}Hf&QU;Nw~B~_R>qrQ!gM_WIF@9kHEVs5*!8ML;$xRd=Ae8 zNC>#X%?XHGb%;jL&{ zu?4FIDD6OV27+S&_&yX%>Qg_ywX)UsC2Mc}@%&VL@+0gBgl!ykPQC+V;8O)l7Bmzj zC*Obn*KbETvbeCoqe9^wps@+96Bz&y(Jupg_gOXlPinp2_{65n$z$g9y_lGf03}2D zD+~S;f^R|Y?eTGla%9mkGSC8dBntnD0JPQ>2=U^CcFbn_)ompszKRnj6a|qD=Sb6rD0RSX4u65{GW9o0ujv2&~#l| z>9m=w*9j{F6}>Rv6so|?CHbPiP8v!j46kcz{Q4HF>@N4nSo2qDzau%M)1wGHJgG-jbol%5$t2gM_B3Xo_)T0tTK pwu%7Q5PIg<+H;{E#r#NF{y!`P{r35POrih)002ovPDHLkV1nccJx2fl literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/ftb_logo_legacy.png b/launcher/resources/multimc/32x32/instances/ftb_logo_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..20df717107b4aa636061772bb785c4116fa10c52 GIT binary patch literal 1607 zcmV-N2Dtf&P)_aP~Lf( zw%m85xumx7A02W@EVZ+vPiCh(;pV9fKvh*5M-_`Z?`>qZ^Kd=fY zKD&AI&z>I~v~OQ|HhwY0=`jqkp{GSF0ZO+2svOiPy+ne z@ezIDzT55y)pIACJ9@VO1prk=Wf2Q5sCp4$bZ)L4C;|rf@18ofUqXoa7{3BEH}tX4 zDwZ#|iHZ<4G{kcbr~)%m9j}hR_{`3ho_Ai80jR5LntnkKrW)0ILXzaO*lECztNM^6 z)wv|80j)p)WS#*tvyjV~sFKY#W`}C;`r`ikf$>uZ?mzJI_dfA0Qj7VZLeNFJvP9KH zgczd%+Q{*R&d%!-k@AU3N{vpsBChF2DWz!>-U&H=o-iwdxVz zEg7AY88{~*%K#)Hpx$#uK0j2-<$9uOQQ_SIo_lI~dhe9#Y}SY@15l0XN~%N+lcyiL z{d2%*=Z)L96x!E)8YnLe;<$5F^i4Dv$kOV&ZY2(h4oRy1RFN@RCa& z-dHSt{*E(e{zSUGmTbbPPDM!45i|jpz4^rMzev57BU`OzX6)(d$=}%df(D(@x>j;u zU$c*GtJVN$2&f2&h|6N~Q{Wz$nD~y0*xQ`Dvm+hn@)6XiQ&fdiST$e*#8;00au=zf zQo=ub`VECmq(1NAyxMc33eFK@Oh{cc1^DB&T4gMRr&_(g=`(F@H-B|<@=+ix)<8_0 zieQP7Q9TRj+#e4f%ZfL|<48U3vcd`J&d3E3mjN&`2V^l?7kO2@rzytw0Z%yRWD$UR z4GK*Paoz<6)KeyK84F}UW;2kDF)E-@)iedr$bp%eb52@f?AI81U3C;_24)vCU`eX% zWU;riLNqQI*=KOhEql2H)s(7ue%(LR+`N9cRN7-A@0l`tZa9QrkuEdu8kZLLp>KBO zg4>T{6K2s8c`3f~iw|((*JoF9vjKRZP}pf^w?()#LM5L9FFNO@60<+QX*RMe#$%d( ze*%~|4HRn*+4p)SAhzt8_J|Nr2^^e{BJ;Q;Rt;8 zqsxMnbWRmA&t9OoBXw1gRQ*0HT3T>yI?HzssGFHBWuTA+G=wGD!Id(Q%i{A{BMV0t zvql6+8e?@!$Gh|zwbJ?jv-k_;S_a_1{ao~aMi(8x-vOSYBEGbglzspJ002ovPDHLk FV1j`^2FL&a literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/gear_legacy.png b/launcher/resources/multimc/32x32/instances/gear_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..da9ba2f9d51e05489a238664b90e88e4212f1c8f GIT binary patch literal 2414 zcmV-!36b`RP)V^V zpxn*>0>TR~WJ^6)OTnTod7~MOl|Zd^p|38S&Cm1rBtC4?;n=Qca%-y+8LsWAnNjYypL##~gJj#*>=71iUKw?umNsze1z<|Dhv=buh* zr_3z`1cb>KvfWXk17rG7U&4v_G>z4pkhZ;y8f_Q z`0B)clWJ|78Xx9jb;+k5KP5ktWZ4r@bM@uczC6bCsp??h>HdDlcOH7QIJ(PcMK|=G z9(Y&g*qb}4mTm^e!h^-BH4o;d)_l3^<$HdAVes6?x{eM8PQ}MI^VRX~Id#^8y;#b+ zOV#1wlqEkjDKH|Y@KBVE7fV3t`yG3CJz5;yg@wSYd?^2|KmF{Q6Vj8LWps;XcvQbM6Ykc zDMy}nOrL~@w+Q#IgD6X*h)@~?Lax1|tWYd1k+blDj+u_grk*rSv>s+!5r^}SCNf4i++7fO|B<=~%`t^LC4@)A%h@YWFWzyfetp{5Wm zsT%?%fkmY+jI)n*pIZ5gfA^-Ex{O%Up2?(tHt*Z^hKST67?#qpsN_t%E)v?USXE3d-QnTXMD$(HMpO|CI`X#TUzvFKX|*PDv5h|4KEB3OfgZ>#`=bQ6W`-+beW#*%7LtY$G&O>C zfp;#rxr2mIZeKQ z^S6w}*+iW8H+Sgk6Qs%ITvjLxpV@q9D5+_X^CtRC@9P81!)#qJp6f=e>J4Kx)OD#i zTSYY?m(vK2s>^I#2#lP;`*O$eAMRCSi!b)>ec%1rsllE;xE#YkTcav}+>=Hj`JBoZ zw~no)+Aw3B!htLB$UVVzvh$7U{MB@4^4Q9Q!`!Qms0UOo}?HxZ`i+S+8Fl_ar&Yd_t!Ml zlT1u3s;Z9w6jWY1mD(+BwLn#U>WlT1>>sbLKl4~|Y~#QHEM9`B$qR$4guy|s14d0N z<)Xqd1*bfFW`!}v*R0X|d9xThz3V`l{{d~LkTetW#F zs$S12%*J6-XOwH_!s`l%P2!RRo+ z5-|0bLcSVwJCZI<$YYF15~rccTpZOdFgjklU8Q#%^Z&Y@@7^82(Ga%4!as%6Ic3Wh g$G(03Zm;M60od$$bfhz$@c;k-07*qoM6N<$f&kONVE_OC literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/gold_legacy.png b/launcher/resources/multimc/32x32/instances/gold_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..593410fac9a7a929696dee968e112d24780a01da GIT binary patch literal 2366 zcmV-E3BmS>P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Y;vNIPDd2KYw@i7d}K@E5{*v@m}NO#0e!-qFx`F|3j)@cz)Q&JIG8MQPV- zNZUT4ov#6h50f9;Rop!k_e8tnn9vT-!&|+BL_4i^eyUcc^(oE!%KH*}u}( z3e9#l-qpRq1w^SbtSnKpXvRO;jliy7!Y|_ASb?3f0d;4cLSb##NDz>RQr#|V|T9fscre}?$x*q(^!u>-*mFU#4#-fQf=t<@#x5mCPt zrPUFgF$RViA-8jw#}n{l)WduPPt-{lP+oP>8BZ}{2&{BvoT(<6ZrUu1DS+sxQ9wyl z$j-KC0|^y7cyi-0oq`ox!64Z}O@T9K>BM5DUzz@jiSS$vOvzv}gCQRu^;74cJgWCH zLS%G2V+C9^lnWSxx$zGx5Muh~jc0(5XVb55`hirzAkQ#IEHLT3rpRnAZQ04Qg&m_@ zTga$T_XG$Ld^5lp3kI||mW?+$5Cvlp#|VNFkdt+k!GK|=3XtTi^`rtt8on_mm}yH) zo<=ir6Nq5zv7aIsN+8QQhJJ#J&`@H_j+s3Nj*5kox88a7-UmODTnaY0z`=(QVvvxc zjV^NZF~k@pCLyuNfh=BvL=j1eRDz>~xf1LXS~6vuUFPg_$T3S!1@bAbaPcLSSfr#% z)mEg5>T9U6N=?nEhD_01HU$$v^58~QGZ+Jft{LKPPVO$| zzT!=B{E9dFjX5=_`#+ddpzf8oH>}lh&Xvj7+Jz&g+G6*0$BCj!lsmRhZ5nU*>gSSj zHI5AANEXEsz2jpj(9<|qe%_F zm~%D97S9C~-V3mj!W{0Krw12z=;~|it^5dCyPporFDS?fA0$1j;*v;2+TTk@V9zzw(Wfvq zIazYa*KD*|!BLy*)_iF}+Ky*3wS_%bh>c|gdv+z$5Q`-Q{ZwM$bmVCG+g zr6j#JE!?+bGJ>xygz!emx52l)jL21^7Kt;f9yq}~%T{~|xC6aa82{Rr?!d~VYx5?_tf8`0i!+%1>c*tbUGJ%!;Khx|Nr6jRmRx;+~A zw-;evk~&XC9QK7QnCpe#ZA3F7?Ukxx8?iTY9>L zu1sM**v?Y*$*K40kOmIbGZS4yuLTceK$LE$Q`%tyhH1-XR1cZx}JW_l-{SB$1^ z|6?Z6K=g7gIMVq&n_u#{A$*XhderAB&-0MTtBrBbI@;c5aP`aCczJ!j2?!NtJXU#X)qy zh}TwB3#5yO@APu#qmNg&+dTVzEX>4Tx0C=2zkv&MmKpe$iTg4)k4(%Z7kfFM0K~%(1t5Adrp;l;lcSTOizPKZb#xU7%UF?eAmTZk_;vXW&X}`>PFL=9Bb# zTZmKh8cJ}t~nO1*4f)jGB+;`-K00006VoOIv0Qdm^0RIx5dxHP~010qNS#tmY z4#NNd4#NS*Z>VGd000McNliruXvdG;^-41d8-@GHD|FaAIky+{v&QiL=lP*@MmY?(2;+u$MN+%`KI z_Pu%YcBA#vsevcr?+sgTCh=Z*GUR`6^AP}GfS$u1E4#P>0GPo!ONhSi*!p04*3;DeCQhK<&f) z*sGQP8K9n#+AZ>N;zwSUtoL@B#xqvOPE(T)s;gE5kvL&jI`#>j4(oySo k8KmZ2o@>8otPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0005mNklmU7q>t}A7K;7Jp?vWyZXE^2^aLhd6cr6?Nx}GzhbAiKn2vCjV^!6~Iu>Q3+s&RUP`s6tmP`fB*CGAac zm+sXWju{EhxqwDot=P$aI>VJjZPXd-y;mjyYmvPVn88RR!lv%jZ*V1*Nbe$7SkAbfkg?Z z?Yhm%*gE%n(KN^MdXaMh8$sI0%2c}wC%1Z*=Yb1>U$$?n!SS0c&j0`b07*qoM6N<$ Ef?KQ%XaE2J literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/herobrine_legacy.png b/launcher/resources/multimc/32x32/instances/herobrine_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..e5460da31d7df8e6d20fa59fbd3b3b8f5ef655df GIT binary patch literal 1034 zcmV+l1oiugP)Bl?e*rgyI8@@mp@8;MKnMh=jZi95rApdJZQ8`mFOoR6cV~7U zhuSkvY@Um)R5k(QVvFbr8fM-MnmH$M*Eb<)3 zu?1kGC}L_?AdX`JOaB}G6Cz03l&mfWsu^xI>I+!VszpvfMU_XJ zC7IMuX5bS>m@xq%0SrulfiaS0ElrceZ7|WTLoe5k*>_{sZc9nJ;5 z?1lW8!;Rf-pCJ8yHdpRA{P9<@pWoceG4u)yT|k!wAD(^FVXJ+Iuh(i0OQ{{+-1aqn zr2K%xf9`l|-h1h^!ykOUu!ryPRiGVG1sFg1m>Um{^AA9fBy{tU-na{U&g8DU+KAi_PYzX`oq_)(IK;7 z=mNSdSXsOV09ajG#OmCPo0q|;!{^U`0iEvkG`RQ^-hOM=jqg^M9G<@X8_Yp_pdFb` zv3b+`rE*6d-cR11+q%8z#;Z5y9qvaC97YRj+W&Q?OE(<;^2f^#zgl^!=gmI)^fQNF zKl8N1grKj0p;ut&0=g_%T&_B7t^W$@{3wlQrX7}n>ba)Y@Kz=VZbAYFD%5?}-ggj5 z0APUl-fxEhBEbYUbGPlUN`|)2J?@MAmupE1AOVmNu@NL~t9}&@#8qPqYL~7zf4+1B zmKc)^gm4FwhyD@am2cPgPcaw<0(Svku}s@a7~K+Zj`jAFi9DiLGAphClriyfX5{|; zp1XqtYcSRcG{@j&lb21`K~)6l!-A_o^1Fswl36y2!5+<+m@S0Z1;m;nQ@yixq}h2m zCE!I_#zwAL;&8+!(hl%&O7|KMp)8mrPieIpq`h($ literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/infinity_legacy.png b/launcher/resources/multimc/32x32/instances/infinity_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..bd94a3dc2dd1a7a9a6d7c9f02e4fd9d33f793e24 GIT binary patch literal 1714 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 Ef}+7KVE_OC literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/meat_legacy.png b/launcher/resources/multimc/32x32/instances/meat_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..6694859db96b06391866d074da591ea12a9a352b GIT binary patch literal 1514 zcmV(abm~6@`EG}0cjCXxL^^* zCMy~wC{01P1>N?DilyR@px_TMh+>iGHm2Lg1ww3tK)X_$tmCX>`_5!MGxy#@aqmQq zcf45#UTHMa=-fHa`#tA<-us3aqsb8h*pgThbOdeHZ+%fF+81pqtM%vp(N{*8EFc5~ zI~s;krBsw^Nx)Dk$R%xG!ANdeU*d&K-?5>t zyrOY25$S}?6HA&dn>NyMVnM@#rg_mcXgX0iGIXHjz?#gV6O0HK5^;(^o%xccMQw$7 zg%!ayrR%sVF$+pPJd@ZJJ=UVf<x25#+P&#G5XPof=606A3m&gr)E`qTzVKpTF4v*OsbpJO8YR+*qDW;=zaG>NpncOFR+m zBa<1PrI2{5h7}D(JyC|E|CvK{MYJffAUCgVMq3LFQ5d)T)V63%uFzLF5m>Gcbnh8pn#wwTEvoI$xv5I?wV)|n{tBGzGzBr)(b-ig2yV4Z;MhY0dGK{gR4q6 zG<20lg6i^pN3Mi#58sH$K?;jf0)WQLwfB!Z+il~SpA0}?YTFN{i|&VC^38tB?}Vo9D> z&J^g%9Xhe-P#;xdr9v(kWFPf<5?4i+G1qxuZPKoveEbK{dHcP&n!*~sn{D_0Bzh)z zqG`{xhC--)AV!;^w58&@sIt;RDKQjvC7xqduvD*@DB#?PE}92g-3>d;DvczPh~!LtnFA^*GS(Z!}@*jOB3{e0TkGqr#5nT Q7ytkO07*qoM6N<$g3;!_-v9sr literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/netherstar_legacy.png b/launcher/resources/multimc/32x32/instances/netherstar_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..43cb51139e7b412d5ca95bbde6d814bc96b695a3 GIT binary patch literal 1942 zcmV;H2Wj|;P)@pYFrCXRUMgK5MTnlv2E5h`zo)0nppqtAID!NW1|7F)%RT z@%em#^73*WKuT#YE-q$zdV1`G5;+h7A%qwl94re4gV9o{^!-w)R4b(fn0Fj!Boc}I zyQZebItYma5f~U4C<}+fCtNPqPa000`h!>3bpXq<%<<9DPlXUSg%A_%?d|4%N$i_I zJRWzKN~Ic?%hl8P_S@HWUDt&W04Sx%=kwOs=;)t-8wVkw?F$`TQpzyU9X)a4_a2W& zAD^BkJ~l=wli^HV9q)E_dXLuDe!Ms{;{b)Jsj10WESCST2?!y?ojZ39xm>QZC+q94 z`F+0d)1@V*=jOK7{OiF3(%CFcjg3BcF!+&^PR|&Iu>`EJ$1UthKq)1Lhll-|rZxF} zzGDc%;>rrqsw%qAodaO_>#w;rG(=@>EoPxm5ptYrA%qTyy$HzN4&T?;Cjselxjag# z0MB=<3{;IH>LQ)Xk+Up>5FXPsH9$V!L%bH`6-6ip1_nHlNF?Am zj_!85U8ZSPYnpcXdo3*=3L(gvCIFiAHvo$A0|0J4evAN4c|3nD7K?w^b$zZ-D3o^7 ziJd7BiA2I14u|WNQXNXEYTLHmnx<6RvcCSqfZrcPDG&k$NFlHb1?*yxwM>Rk<{|(q zftwuh`x#wWV0>W#rΠ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 V|6)f+;rjpp002ovPDHLkV1iNGT7dun literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/skeleton_legacy.png b/launcher/resources/multimc/32x32/instances/skeleton_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..0c8d3505ab44d363cad65fff3c0eaa4a020c9851 GIT binary patch literal 696 zcmV;p0!RIcP)_>(Orfj25xK{%Pg!TrW2U?6bnAU zm^cQL8E)hw7-=BFuwXE3(0NZL2pWsn4PKzE4Rp`ccw2?ho*$b2tS+j4{!`U$p;8JE z5zFPW0XI~u)mkg16aoO2N+lB!zey?0ZQIp^h>9`hE2V~=PRGYut!6lmV>8Bn0)UN( zS4~1GwFUrJO8G07%emZjT{H~CM8wQ&Hv8^*9=oOi%d(^pLK%i(x~_|S>k0vYd7j5A zl?ruUtyaX5NQ8JYnGnzCbK-C~Ol+Dav6Pay*=#Z)1p8O&n=ZUV3futSZO~)awOA~O zlgT7;B9S20qaF+f#LMOKsDZ!}2;9J433O$%Sz^ZCcc&17Sl(*An}VzC(UkNbOo$43C; zJt+SC4E=tee(SO4?(Pou?{>RK4V)|i=NynRfTyR;Wf)K_7C)^&z!@a~fX!gqB%j%T z@9AhXBL4mILL7}oH}LQPyk9YZY&f{?iVX}N2Xg_=vv_Ku}<`KO$NnDzdk~M^qqM;NawtXn|mX zr#|yko@0T%@gC74!2;`B*NBS56j<9_%VP`N3~ymU+}DEPs9KgOHG%X4yeQWEuN&%ef%3L3y1%z&xjQ*VA6r3}2Yy_3mE3j&grxJ$}X zr@*?0Z&mOW93Hsk#9KAP5cS4e9=_q>i_T%dU%mK4O#$zeP+C?%R%DdbWzkg!U&isB z@8!wAdfjo`X#ig0Z-EU5UviFbxAW^a95?+X;L8e_ti)}+KQ8g~fa{f~2UZ=7KOC2& zJUl)u<5fY`^Hm3U2h;Ou0e@`cG2|c00V~PZWqMQ~0dG5T^$kZQxb209LEV>%P@n?d z_2SpBJCcK2UL;2W@BelUpj9B1xc0Mn4*a>!z*m6^aN-eW6alzd!QZ@?c{q0v1d76v zAog=$kc6UMt&kblcdnNOU%B&YiSOV4`A!+%{@PENM@X%V0t|TWFW)a<2%N|o=%FLvMMK(|;^!Ii4-eul*!cvlI- z5Ey5ZGol5~CJzdk(Rj3IItKmIKmQ;#Qtd9k}yt> zPA|`ae13}qYwf@lMFawHLBGZqyR>8+%wcT>cS|e}?jM#|KKg+=51XhO2c{3M^@NFto444TC3=pvIb zF+Z83ly`oii>!u;@E{dpHc=Zw2$P6%vn0;)z?-M4D(a?bNax1!d#6a^!Pykrb~&HT zvu_(5aSE1Yd~h@uJU0s!Q@~DmKBJ{2^q+Ynazu5Ou`I+DpoWNnSf2jj-EH zr~f)zSZU{U&XwoXEh3n?MI(?Ft+juG?Dr~ZF?MYesjWs$n}kHTI1T(SFHZ#({0DPf VE&;%r6@~x+002ovPDHLkV1iLx3Zei2 literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/steve_legacy.png b/launcher/resources/multimc/32x32/instances/steve_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..07c6acdeeb9ba480f86db105b6493f168e7722c9 GIT binary patch literal 969 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 Ef}{X{S^xk5 literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/32x32/instances/tnt_legacy.png b/launcher/resources/multimc/32x32/instances/tnt_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..e40d404d8f9c0e247f2579761f53954889a050a0 GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@f>I@}5hcO-X(i=}MX3w{iJ5sNdVa1U3T4K6 zrh0}3249L60#&{Aba4#v@J`iG-NDVry$^H5};?7#duIbmUq!X0hjF;4_X==5NP8{IOXJ0&}p0?<&w%hp*Q&e zgFs}5!7{Iu$dt7L?#`(%Qy7aZly-|>?wwJ0NVVxvvtuHo-u-~H>>`mHWgl$n?E~CP#fTzrG=k;96XkO^H zIEb4s+ADK106k22l^fZH~tDnm{ Hr-UW|wFG@A literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/50x50/instances/enderman_legacy.png b/launcher/resources/multimc/50x50/instances/enderman_legacy.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3a72b3a093df63a45e6ca6a651bedb0943de8a GIT binary patch literal 2429 zcmbVOdo)!09-ro*u5-MH9yhg{sdOC7-Xp`zC1#9g5gJTfGB0Cf9%cq(iYOZCm>f~7 z;}PSQdl1Qu9^{Hr6sA&9UxGV>B zpp6&kC3I#-aNT7h_AZ$>l_}fHB(Ttq+W>nh5mvxsOXz@<7sVG7r4Hy%dWrCUQH(_c zpCFRG4(QKL(Y$;BXMu+f=>?U2#G{U#A4&(;;iDVtpp+t79tP`SP+NB;VfZE{r5LNBkciuvL%iGovxmC}V+$O^>rc#Cm; zLW?DS?Ekp&O|+OAFJxo=*kVD9hzZvt9J54*bN9!F7J;xg#GN88Toif~MZk>VvH21Y ziUS(Hv0`yqM9|s}gdp3kpe+u>+d&Wk0tt2$TZrTW5y;lg~*^f_J%Tf~iJvs^_29`Gr{MD8~{0B!zPCCAkxEp5eUsP4+@DY?NJ9^1>y<66*tIe~L+zC1X-fKxdTuj7`&l3!4eICi-260YPz+;>X6TSR^OmfdrXLRw1DUHq-vEL(A%T10Wkp4h_rs1JkPZY&}XZQ2La2-{u zp>1c!RHm+;(qA(r?4XhYppNOsp`30Ze9SH%9^**rM0eg^9KR!A3f3Eypsr_^JyhCc zP_FnJ(WD}ap`V9Q2b`U{7``ZfH_!C(xyvh)%NpIa4r&5NGh-f1PEKx5X{r;6>o~c? zQ&UwMX|Hx7%R6@{Of&Fle0>oXMPIP>YcC>+2OT?xB#TNvS-|-!$IDZDwZ1v!G|r zsBBDzI6p5N=(N=#J&+d)Luw3s&nw~|>TMFIsCA+@xrCovtqDbH@~*ZrdOqA#8e`74 zKa?wlBMmgNnNUIAKl)g6 z`({Q;I?|mPJ^AIBIrUq&+u_2;y&bP&Lhr2}cJGUAf0*HsFte|CboaRj3x(~=?G4UF z)pyAvow7`op&I>{3*P6{J%52bD>9xo_$lyr6WiMp-Sy0Xw$SCtqh`gbv6X@N_|%N5 zmoFct@N!Rwt;@&s+;XILU*zj~i)T8gJ6X8oWAl$=@@Ic4>W}dO?K_FiQ9&r)lZnF2 z-2sk8gOWXaRet1LPe(>&IOF?(8`M(FZ%z#PZ{ZHzk*OwB$D)*;C_h_fwtjkUshx6C zJsg55)4a^-=a{{f)%$7PqFHF%Is8$2AUfJt^tZGwix$+11h+w2TS1y}wYefiIg+)0 z>K90_#QTkfd`&dv$fF-1lm6Z0GBPTos76(EbFJ#JnO1s(%(G(|!!l$Ag{g4jug3KD z_U2T?y*rhZh$I%Lpx65XbwXytW`_FF)XQBl=z3$f?CA}rszPC~V#{b5rL?L`JDzUS zQ2sHP$jETMZ{HAn@<57-U$3g|qCOo0WIh4iWqDD9V{g^!@^|gYG`AQe;ZkBilA@Wf zJFD74LFs8!txYCNGMk=^F>;&DYX|5qcl(gz+?J7mR4|6_4eW27NE*t`ZglDRouxZ2 zF+&|XbM~zNh^^N3?YAzLK$S@3@uXD?`9|`}1-BM*^Sghp4eXu8C-C`MuZ^M>+V5W~ zZFK|G{msiOeojcdcQ^EA^_$t2<{U?RPI||E6Ze)w%glsF9bWDpI`DpSdAhw_^d!OB zERgIJR3QA2TKxL@hwD{Y4MLRcf~xJ~$B{SJ3mP<6A=(s3y>g;K&EgM{hl@9*f*f}6 E-vjLh{r~^~ literal 0 HcmV?d00001 diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 391a974f..0627fc47 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -254,13 +254,11 @@ scalable/discord.svg - - + scalable/instances/chicken.svg scalable/instances/creeper.svg scalable/instances/enderpearl.svg scalable/instances/ftb_logo.svg - scalable/instances/flame.svg scalable/instances/gear.svg scalable/instances/herobrine.svg @@ -270,23 +268,78 @@ scalable/instances/skeleton.svg scalable/instances/squarecreeper.svg scalable/instances/steve.svg - - scalable/instances/brick.svg scalable/instances/diamond.svg scalable/instances/dirt.svg - scalable/instances/gold.svg scalable/instances/grass.svg + scalable/instances/brick.svg + scalable/instances/gold.svg scalable/instances/iron.svg scalable/instances/planks.svg scalable/instances/stone.svg scalable/instances/tnt.svg - scalable/instances/enderman.svg - scalable/instances/fox.svg scalable/instances/bee.svg - scalable/instances/prismlauncher.svg + + + 32x32/instances/chicken_legacy.png + 128x128/instances/chicken_legacy.png + + 32x32/instances/creeper_legacy.png + 128x128/instances/creeper_legacy.png + + 32x32/instances/enderpearl_legacy.png + 128x128/instances/enderpearl_legacy.png + + 32x32/instances/ftb_glow.png + 128x128/instances/ftb_glow.png + + 32x32/instances/ftb_logo_legacy.png + 128x128/instances/ftb_logo_legacy.png + + 128x128/instances/flame_legacy.png + + 32x32/instances/gear_legacy.png + 128x128/instances/gear_legacy.png + + 32x32/instances/herobrine_legacy.png + 128x128/instances/herobrine_legacy.png + + 32x32/instances/magitech_legacy.png + 128x128/instances/magitech_legacy.png + + 32x32/instances/meat_legacy.png + 128x128/instances/meat_legacy.png + + 32x32/instances/netherstar_legacy.png + 128x128/instances/netherstar_legacy.png + + 32x32/instances/skeleton_legacy.png + 128x128/instances/skeleton_legacy.png + + 32x32/instances/squarecreeper_legacy.png + 128x128/instances/squarecreeper_legacy.png + + 32x32/instances/steve_legacy.png + 128x128/instances/steve_legacy.png + + 32x32/instances/brick_legacy.png + 32x32/instances/diamond_legacy.png + 32x32/instances/dirt_legacy.png + 32x32/instances/gold_legacy.png + 32x32/instances/grass_legacy.png + 32x32/instances/iron_legacy.png + 32x32/instances/planks_legacy.png + 32x32/instances/stone_legacy.png + 32x32/instances/tnt_legacy.png + + 50x50/instances/enderman_legacy.png + + scalable/instances/prismlauncher.svg + scalable/instances/fox_legacy.svg + scalable/instances/bee_legacy.svg + scalable/delete.svg scalable/tag.svg diff --git a/launcher/resources/multimc/scalable/instances/bee_legacy.svg b/launcher/resources/multimc/scalable/instances/bee_legacy.svg new file mode 100644 index 00000000..49f216c8 --- /dev/null +++ b/launcher/resources/multimc/scalable/instances/bee_legacy.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/scalable/instances/fox.svg b/launcher/resources/multimc/scalable/instances/fox.svg index 687a5b09..f4b2bf53 100644 --- a/launcher/resources/multimc/scalable/instances/fox.svg +++ b/launcher/resources/multimc/scalable/instances/fox.svg @@ -35,23 +35,15 @@ y1="11" x2="171" y2="7" - gradientUnits="userSpaceOnUse" /> + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/launcher/resources/multimc/scalable/instances/meat.svg b/launcher/resources/multimc/scalable/instances/meat.svg index c13fa371..52da5b1c 100644 --- a/launcher/resources/multimc/scalable/instances/meat.svg +++ b/launcher/resources/multimc/scalable/instances/meat.svg @@ -72,7 +72,7 @@ x="397.64703" y="-389.8125" ry="4" - rx="0" + rx="4" transform="rotate(45)" /> Date: Tue, 22 Nov 2022 17:36:47 -0300 Subject: [PATCH 165/277] forgot ftb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- .../multimc/128x128/instances/ftb_glow.png | Bin 0 -> 12708 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 launcher/resources/multimc/128x128/instances/ftb_glow.png diff --git a/launcher/resources/multimc/128x128/instances/ftb_glow.png b/launcher/resources/multimc/128x128/instances/ftb_glow.png new file mode 100644 index 0000000000000000000000000000000000000000..86632b21ded4a18156a011115bdf671a8c998cce GIT binary patch literal 12708 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�jIYC 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 Date: Thu, 24 Nov 2022 19:40:03 -0300 Subject: [PATCH 166/277] resize to 24x24 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- .../multimc/scalable/instances/bee.svg | 47 +++--- .../multimc/scalable/instances/brick.svg | 16 +- .../multimc/scalable/instances/chicken.svg | 49 +++--- .../multimc/scalable/instances/creeper.svg | 16 +- .../multimc/scalable/instances/diamond.svg | 16 +- .../multimc/scalable/instances/dirt.svg | 18 +-- .../multimc/scalable/instances/enderman.svg | 16 +- .../multimc/scalable/instances/enderpearl.svg | 29 ++-- .../multimc/scalable/instances/flame.svg | 23 +-- .../multimc/scalable/instances/fox.svg | 36 ++--- .../multimc/scalable/instances/ftb_logo.svg | 55 ++++--- .../multimc/scalable/instances/gear.svg | 16 +- .../multimc/scalable/instances/gold.svg | 16 +- .../multimc/scalable/instances/grass.svg | 16 +- .../multimc/scalable/instances/herobrine.svg | 57 +++---- .../multimc/scalable/instances/iron.svg | 16 +- .../multimc/scalable/instances/magitech.svg | 26 +-- .../multimc/scalable/instances/meat.svg | 18 +-- .../multimc/scalable/instances/netherstar.svg | 49 ++++-- .../multimc/scalable/instances/planks.svg | 16 +- .../multimc/scalable/instances/skeleton.svg | 16 +- .../scalable/instances/squarecreeper.svg | 16 +- .../multimc/scalable/instances/steve.svg | 152 +++++++++--------- .../multimc/scalable/instances/stone.svg | 16 +- .../multimc/scalable/instances/tnt.svg | 16 +- 25 files changed, 393 insertions(+), 374 deletions(-) diff --git a/launcher/resources/multimc/scalable/instances/bee.svg b/launcher/resources/multimc/scalable/instances/bee.svg index fef2aeb3..110b224c 100644 --- a/launcher/resources/multimc/scalable/instances/bee.svg +++ b/launcher/resources/multimc/scalable/instances/bee.svg @@ -2,10 +2,10 @@ Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="4" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/brick.svg b/launcher/resources/multimc/scalable/instances/brick.svg index 8264bb43..b600eba8 100644 --- a/launcher/resources/multimc/scalable/instances/brick.svg +++ b/launcher/resources/multimc/scalable/instances/brick.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/creeper.svg b/launcher/resources/multimc/scalable/instances/creeper.svg index 38f7ac7a..4a9fe380 100644 --- a/launcher/resources/multimc/scalable/instances/creeper.svg +++ b/launcher/resources/multimc/scalable/instances/creeper.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="100" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/diamond.svg b/launcher/resources/multimc/scalable/instances/diamond.svg index e1e1380c..1d490b91 100644 --- a/launcher/resources/multimc/scalable/instances/diamond.svg +++ b/launcher/resources/multimc/scalable/instances/diamond.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/enderman.svg b/launcher/resources/multimc/scalable/instances/enderman.svg index 483a002e..29f25a2f 100644 --- a/launcher/resources/multimc/scalable/instances/enderman.svg +++ b/launcher/resources/multimc/scalable/instances/enderman.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="228" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/enderpearl.svg b/launcher/resources/multimc/scalable/instances/enderpearl.svg index dc4b80f1..e4c1e104 100644 --- a/launcher/resources/multimc/scalable/instances/enderpearl.svg +++ b/launcher/resources/multimc/scalable/instances/enderpearl.svg @@ -2,10 +2,10 @@ </defs><title id="title132">Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="260" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/flame.svg b/launcher/resources/multimc/scalable/instances/flame.svg index b15fa3ff..775914b8 100644 --- a/launcher/resources/multimc/scalable/instances/flame.svg +++ b/launcher/resources/multimc/scalable/instances/flame.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + d="M 305.94922,10 C 304.86851,10.02738 304,10.912691 304,12 v 1 a 1,1 0 0 1 -1,-1 v 2 c 0,0.552284 0.44772,1 1,1 0,0.552284 -0.44772,1 -1,1 -0.55228,0 -1,-0.447716 -1,-1 -0.55228,0 -1,0.447716 -1,1 0,1.104568 0.89543,2 2,2 0.55228,0 1,0.447716 1,1 0,-0.552284 0.44772,-1 1,-1 h 1 c 0.55228,0 1,-0.447716 1,-1 v -1 c 0,0.552284 -0.44772,1 -1,1 -0.55228,0 -1,-0.447716 -1,-1 0.55228,0 1,-0.447716 1,-1 v -1 c 0,-0.552284 -0.44772,-1 -1,-1 v -1 c 0,-1.087309 0.86851,-1.97262 1.94922,-2 H 306 c -0.0173,0 -0.0336,-4.35e-4 -0.0508,0 z" /> diff --git a/launcher/resources/multimc/scalable/instances/fox.svg b/launcher/resources/multimc/scalable/instances/fox.svg index f4b2bf53..95ca6ef9 100644 --- a/launcher/resources/multimc/scalable/instances/fox.svg +++ b/launcher/resources/multimc/scalable/instances/fox.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="164" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/ftb_logo.svg b/launcher/resources/multimc/scalable/instances/ftb_logo.svg index edaed84e..85e8295e 100644 --- a/launcher/resources/multimc/scalable/instances/ftb_logo.svg +++ b/launcher/resources/multimc/scalable/instances/ftb_logo.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/gear.svg b/launcher/resources/multimc/scalable/instances/gear.svg index 4bb91e08..b2923d67 100644 --- a/launcher/resources/multimc/scalable/instances/gear.svg +++ b/launcher/resources/multimc/scalable/instances/gear.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="356" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/gold.svg b/launcher/resources/multimc/scalable/instances/gold.svg index 0dd246c5..f1513d70 100644 --- a/launcher/resources/multimc/scalable/instances/gold.svg +++ b/launcher/resources/multimc/scalable/instances/gold.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher diff --git a/launcher/resources/multimc/scalable/instances/iron.svg b/launcher/resources/multimc/scalable/instances/iron.svg index b2042694..6a6faf77 100644 --- a/launcher/resources/multimc/scalable/instances/iron.svg +++ b/launcher/resources/multimc/scalable/instances/iron.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="516" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/meat.svg b/launcher/resources/multimc/scalable/instances/meat.svg index 52da5b1c..0fbfe5a4 100644 --- a/launcher/resources/multimc/scalable/instances/meat.svg +++ b/launcher/resources/multimc/scalable/instances/meat.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="548" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/netherstar.svg b/launcher/resources/multimc/scalable/instances/netherstar.svg index 152b5b1c..a5d9606e 100644 --- a/launcher/resources/multimc/scalable/instances/netherstar.svg +++ b/launcher/resources/multimc/scalable/instances/netherstar.svg @@ -2,10 +2,10 @@ Prism Launcher LogoPrism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="612" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/planks.svg b/launcher/resources/multimc/scalable/instances/planks.svg index aad6b8d2..8febfa6b 100644 --- a/launcher/resources/multimc/scalable/instances/planks.svg +++ b/launcher/resources/multimc/scalable/instances/planks.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="644" + y="4" /> diff --git a/launcher/resources/multimc/scalable/instances/squarecreeper.svg b/launcher/resources/multimc/scalable/instances/squarecreeper.svg index 7b9cffdd..ddb9aec8 100644 --- a/launcher/resources/multimc/scalable/instances/squarecreeper.svg +++ b/launcher/resources/multimc/scalable/instances/squarecreeper.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + y="17" /> diff --git a/launcher/resources/multimc/scalable/instances/stone.svg b/launcher/resources/multimc/scalable/instances/stone.svg index 5f8bd526..6df534d2 100644 --- a/launcher/resources/multimc/scalable/instances/stone.svg +++ b/launcher/resources/multimc/scalable/instances/stone.svg @@ -2,10 +2,10 @@ Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher Prism Launcher Logo19/10/2022Prism LauncherAutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zekehttps://github.com/PrismLauncher/PrismLauncherCC BY-SA 4.0Prism Launcher + width="24" + height="24" + x="804" + y="4" /> From 71d35dc70b58b14820abeeffd8a8aed067379c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20C=C3=A9zar?= Date: Thu, 24 Nov 2022 19:43:24 -0300 Subject: [PATCH 167/277] meat is now round MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- .../multimc/scalable/instances/meat.svg | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/launcher/resources/multimc/scalable/instances/meat.svg b/launcher/resources/multimc/scalable/instances/meat.svg index 0fbfe5a4..36f0551b 100644 --- a/launcher/resources/multimc/scalable/instances/meat.svg +++ b/launcher/resources/multimc/scalable/instances/meat.svg @@ -8,12 +8,34 @@ viewBox="0 0 24 24" id="svg168" xml:space="preserve" + sodipodi:docname="meat.svg" + inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" - xmlns:dc="http://purl.org/dc/elements/1.1/"> Date: Fri, 25 Nov 2022 14:18:19 -0300 Subject: [PATCH 168/277] remove unused ftb icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- .../128x128/instances/ftb_glow_legacy.png | Bin 12708 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 launcher/resources/multimc/128x128/instances/ftb_glow_legacy.png diff --git a/launcher/resources/multimc/128x128/instances/ftb_glow_legacy.png b/launcher/resources/multimc/128x128/instances/ftb_glow_legacy.png deleted file mode 100644 index 86632b21ded4a18156a011115bdf671a8c998cce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12708 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�jIYC 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 Date: Sat, 26 Nov 2022 15:05:08 +0100 Subject: [PATCH 169/277] fix: use monochrome icon for breeze Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .../breeze_dark/scalable/shortcut.svg | 31 ++++++++++--------- .../breeze_light/scalable/shortcut.svg | 31 ++++++++++--------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/launcher/resources/breeze_dark/scalable/shortcut.svg b/launcher/resources/breeze_dark/scalable/shortcut.svg index 2f24b8c0..5559be1d 100644 --- a/launcher/resources/breeze_dark/scalable/shortcut.svg +++ b/launcher/resources/breeze_dark/scalable/shortcut.svg @@ -1,15 +1,18 @@ - - - - - - - - + + + + + + + diff --git a/launcher/resources/breeze_light/scalable/shortcut.svg b/launcher/resources/breeze_light/scalable/shortcut.svg index 96def1f8..426769d1 100644 --- a/launcher/resources/breeze_light/scalable/shortcut.svg +++ b/launcher/resources/breeze_light/scalable/shortcut.svg @@ -1,15 +1,18 @@ - - - - - - - - + + + + + + + From 6c09efd6306705fd4cfebdb66710373c90f5ab3a Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sun, 27 Nov 2022 15:24:15 +0100 Subject: [PATCH 170/277] fix: use qt 6.4.0 on windows msvc for now qt 6.4.1 has a very bad scaling regression Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f07a86e6..79221531 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,7 +67,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.4.1' + qt_version: '6.4.0' qt_modules: 'qt5compat qtimageformats' qt_tools: '' From 236c196e681dcbf0547677260d141a7e48047b43 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 27 Nov 2022 18:38:56 +0100 Subject: [PATCH 171/277] fix: improve code readability Signed-off-by: Sefa Eyeoglu --- launcher/ui/MainWindow.cpp | 56 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 5d2a07f3..c3c4d10f 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1801,39 +1801,37 @@ void MainWindow::on_actionAddInstance_triggered() void MainWindow::droppedURLs(QList urls) { + // NOTE: This loop only processes one dropped file! for (auto& url : urls) { - if (url.isLocalFile()) { - auto localFileName = url.toLocalFile(); - QFileInfo localFileInfo(localFileName); - - ImportResourcePackDialog dlg(this); - - if (ResourcePackUtils::validate(localFileInfo)) { - dlg.exec(); - - if (dlg.result() == QDialog::Accepted) { - qDebug() << "Selected instance to import resource pack into: " << dlg.selectedInstanceKey; - auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); - auto instanceButBuffed = std::dynamic_pointer_cast(instance); - instanceButBuffed->resourcePackList()->installResource(localFileName); - } - } else if (TexturePackUtils::validate(localFileInfo)) { - dlg.exec(); - - if (dlg.result() == QDialog::Accepted) { - qDebug() << "Selected instance to import texture pack into: " << dlg.selectedInstanceKey; - auto instance = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); - auto instanceButBuffed = std::dynamic_pointer_cast(instance); - instanceButBuffed->texturePackList()->installResource(localFileName); - } - } else { - addInstance(localFileName); - } - } else { + if (!url.isLocalFile()) { // probably instance/modpack addInstance(url.toString()); + break; } - // Only process one dropped file... + auto localFileName = url.toLocalFile(); + QFileInfo localFileInfo(localFileName); + + bool isResourcePack = ResourcePackUtils::validate(localFileInfo); + bool isTexturePack = TexturePackUtils::validate(localFileInfo); + + if (!isResourcePack && !isTexturePack) { // probably instance/modpack + addInstance(localFileName); + break; + } + + ImportResourcePackDialog dlg(this); + + if (dlg.exec() != QDialog::Accepted) + break; + + qDebug() << "Adding resource/texture pack" << localFileName << "to" << dlg.selectedInstanceKey; + + auto inst = APPLICATION->instances()->getInstanceById(dlg.selectedInstanceKey); + auto minecraftInst = std::dynamic_pointer_cast(inst); + if (isResourcePack) + minecraftInst->resourcePackList()->installResource(localFileName); + else if (isTexturePack) + minecraftInst->texturePackList()->installResource(localFileName); break; } } From 8cac61f0bea17036346438761728c3b7bef4fbeb Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 27 Nov 2022 18:41:52 +0100 Subject: [PATCH 172/277] fix: check resource pack validity solely using pack format Signed-off-by: Sefa Eyeoglu --- launcher/minecraft/mod/ResourcePack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index ef0370d0..4a9ad21b 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -117,5 +117,5 @@ bool ResourcePack::applyFilter(QRegularExpression filter) const bool ResourcePack::valid() const { - return s_pack_format_versions.contains(m_pack_format); + return m_pack_format != 0; } From 3cbf8d4993b8c336feb65e6371b777ab0d7865ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20C=C3=A9zar?= Date: Sun, 27 Nov 2022 16:24:45 -0300 Subject: [PATCH 173/277] resolving reviews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santiago Cézar --- launcher/resources/multimc/index.theme | 2 +- launcher/resources/multimc/multimc.qrc | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/launcher/resources/multimc/index.theme b/launcher/resources/multimc/index.theme index 1f72c0a7..070e23f1 100644 --- a/launcher/resources/multimc/index.theme +++ b/launcher/resources/multimc/index.theme @@ -2,7 +2,7 @@ Name=multimc Comment=Default Icons Inherits=default -Directories=8x8,16x16,22x22,24x24,32x32,48x48,64x64,256x256,scalable,scalable/instances +Directories=8x8,16x16,22x22,24x24,32x32,32x32/instances,48x48,50x50/instances,64x64,128x128/instances,256x256,scalable,scalable/instances [8x8] Size=8 diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 0627fc47..08b86406 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -6,9 +6,6 @@ scalable/reddit-alien.svg - - scalable/instances/flame.svg - scalable/launcher.svg @@ -254,7 +251,8 @@ scalable/discord.svg - + + scalable/instances/flame.svg scalable/instances/chicken.svg scalable/instances/creeper.svg scalable/instances/enderpearl.svg @@ -285,7 +283,7 @@ 32x32/instances/chicken_legacy.png 128x128/instances/chicken_legacy.png - + 32x32/instances/creeper_legacy.png 128x128/instances/creeper_legacy.png From a5b0b4800a592172ba4ff6c6421bc0f0abdef5e0 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 27 Nov 2022 23:10:32 +0100 Subject: [PATCH 174/277] fix: make resource buttons work when instance is running Signed-off-by: Sefa Eyeoglu --- launcher/ui/pages/instance/ExternalResourcesPage.cpp | 11 ----------- launcher/ui/pages/instance/ExternalResourcesPage.h | 1 - launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- launcher/ui/pages/instance/ModFolderPage.h | 2 +- 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index b6c873cc..5c919573 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -14,8 +14,6 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared { ui->setupUi(this); - ExternalResourcesPage::runningStateChanged(m_instance && m_instance->isRunning()); - ui->actionsToolbar->insertSpacer(ui->actionViewConfigs); m_filterModel = model->createFilterProxyModel(this); @@ -45,7 +43,6 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared auto selection_model = ui->treeView->selectionModel(); connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); - connect(m_instance, &BaseInstance::runningStatusChanged, this, &ExternalResourcesPage::runningStateChanged); } ExternalResourcesPage::~ExternalResourcesPage() @@ -97,14 +94,6 @@ void ExternalResourcesPage::filterTextChanged(const QString& newContents) m_filterModel->setFilterRegularExpression(m_viewFilter); } -void ExternalResourcesPage::runningStateChanged(bool running) -{ - if (m_controlsEnabled == !running) - return; - - m_controlsEnabled = !running; -} - bool ExternalResourcesPage::shouldDisplay() const { return true; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index 8e352cef..11058bf6 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -47,7 +47,6 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { protected slots: void itemActivated(const QModelIndex& index); void filterTextChanged(const QString& newContents); - virtual void runningStateChanged(bool running); virtual void addItem(); virtual void removeItem(); diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index f0106066..0a2e6155 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -108,13 +108,13 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr disconnect(mods.get(), &ModFolderModel::updateFinished, this, 0); }); + connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged); ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning()); } } void ModFolderPage::runningStateChanged(bool running) { - ExternalResourcesPage::runningStateChanged(running); ui->actionDownloadItem->setEnabled(!running); ui->actionUpdateItem->setEnabled(!running); ui->actionAddItem->setEnabled(!running); diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index c9a55bde..f20adf34 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -53,12 +53,12 @@ class ModFolderPage : public ExternalResourcesPage { virtual QString helpPage() const override { return "Loader-mods"; } virtual bool shouldDisplay() const override; - void runningStateChanged(bool running) override; public slots: bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; private slots: + void runningStateChanged(bool running); void removeItem() override; void installMods(); From 4e27e132a06974854f1c49dd8fb7a776915f5d03 Mon Sep 17 00:00:00 2001 From: flow Date: Mon, 28 Nov 2022 12:02:38 -0300 Subject: [PATCH 175/277] fix: don't disable interaction on non-mod models when inst. is running It is still useful for mods though, since we don't wnt to allow disabling mods when the instance is running, as that wouldn't affect the running instance, possibly causing confusion Signed-off-by: flow --- launcher/minecraft/MinecraftInstance.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 70d0b949..a3adb268 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1110,8 +1110,6 @@ std::shared_ptr MinecraftInstance::resourcePackList() c if (!m_resource_pack_list) { m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir())); - m_resource_pack_list->enableInteraction(!isRunning()); - connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ResourcePackFolderModel::disableInteraction); } return m_resource_pack_list; } @@ -1121,8 +1119,6 @@ std::shared_ptr MinecraftInstance::texturePackList() con if (!m_texture_pack_list) { m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir())); - m_texture_pack_list->disableInteraction(isRunning()); - connect(this, &BaseInstance::runningStatusChanged, m_texture_pack_list.get(), &ModFolderModel::disableInteraction); } return m_texture_pack_list; } @@ -1132,8 +1128,6 @@ std::shared_ptr MinecraftInstance::shaderPackList() const if (!m_shader_pack_list) { m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir())); - m_shader_pack_list->disableInteraction(isRunning()); - connect(this, &BaseInstance::runningStatusChanged, m_shader_pack_list.get(), &ModFolderModel::disableInteraction); } return m_shader_pack_list; } From 31dc82b1a64fef40ab8fadbbb80d7641456bbb0a Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 29 Nov 2022 09:43:08 +0000 Subject: [PATCH 176/277] Make requested changes Signed-off-by: TheKodeToad --- launcher/minecraft/PackProfile.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index bbdf51d8..43fa3f8d 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -838,15 +838,12 @@ bool PackProfile::installJarMods_internal(QStringList filepaths) QFileInfo sourceInfo(filepath); QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); QString target_filename = id + ".jar"; - QString target_id = "org.multimc.jarmod." + id; + QString target_id = "custom.jarmod." + id; QString target_name = sourceInfo.completeBaseName() + " (jar mod)"; QString finalPath = FS::PathCombine(d->m_instance->jarModsDir(), target_filename); QFileInfo targetInfo(finalPath); - if(targetInfo.exists()) - { - return false; - } + Q_ASSERT(!targetInfo.exists()); if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath())) { @@ -855,7 +852,7 @@ bool PackProfile::installJarMods_internal(QStringList filepaths) auto f = std::make_shared(); auto jarMod = std::make_shared(); - jarMod->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1")); + jarMod->setRawName(GradleSpecifier("custom.jarmods:" + id + ":1")); jarMod->setFilename(target_filename); jarMod->setDisplayName(sourceInfo.completeBaseName()); jarMod->setHint("local"); @@ -895,7 +892,7 @@ bool PackProfile::installCustomJar_internal(QString filepath) return false; } - auto specifier = GradleSpecifier("org.multimc:customjar:1"); + auto specifier = GradleSpecifier("custom:customjar:1"); QFileInfo sourceInfo(filepath); QString target_filename = specifier.getFileName(); QString target_id = specifier.artifactId(); @@ -957,13 +954,12 @@ bool PackProfile::installAgents_internal(QStringList filepaths) const QFileInfo sourceInfo(source); const QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); const QString targetBaseName = id + ".jar"; - const QString targetId = "org.prismlauncher.agent." + id; + const QString targetId = "custom.agent." + id; const QString targetName = sourceInfo.completeBaseName() + " (agent)"; const QString target = FS::PathCombine(d->m_instance->getLocalLibraryPath(), targetBaseName); const QFileInfo targetInfo(target); - if (targetInfo.exists()) - return false; + Q_ASSERT(!targetInfo.exists()); if (!QFile::copy(source, target)) return false; @@ -972,7 +968,7 @@ bool PackProfile::installAgents_internal(QStringList filepaths) auto agent = std::make_shared(); - agent->setRawName("org.prismlauncher.agents:" + id + ":1"); + agent->setRawName("custom.agents:" + id + ":1"); agent->setFilename(targetBaseName); agent->setDisplayName(sourceInfo.completeBaseName()); agent->setHint("local"); From 52d06ed8147bffc191d01c9eddc9aec050a5998d Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Tue, 29 Nov 2022 20:07:00 +0100 Subject: [PATCH 177/277] chore: move instance_icons.svg Signed-off-by: Sefa Eyeoglu --- {launcher/resources => program_info}/instance_icons.svg | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {launcher/resources => program_info}/instance_icons.svg (100%) diff --git a/launcher/resources/instance_icons.svg b/program_info/instance_icons.svg similarity index 100% rename from launcher/resources/instance_icons.svg rename to program_info/instance_icons.svg From 5efa725e92e87497287408c8d4d011e63aa214bb Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sat, 5 Nov 2022 08:25:10 +0100 Subject: [PATCH 178/277] feat(actions) add arm64 build Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 53 ++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 79221531..d957f627 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,18 @@ jobs: qt_modules: 'qt5compat qtimageformats' qt_tools: '' + - os: windows-2022 + name: "Windows-MSVC-arm64" + msystem: '' + architecture: 'arm64' + vcvars_arch: 'amd64_arm64' + qt_ver: 6 + qt_host: windows + qt_arch: 'win64_msvc2019_arm64' + qt_version: '6.4.0' + qt_modules: 'qt5compat qtimageformats' + qt_tools: '' + - os: macos-12 name: macOS macosx_deployment_target: 10.15 @@ -139,7 +151,7 @@ jobs: if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' uses: hendrikmuhs/ccache-action@v1.2.5 with: - key: ${{ matrix.os }}-qt${{ matrix.qt_ver }} + key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} - name: Setup ccache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' @@ -188,8 +200,23 @@ jobs: if: runner.os == 'Linux' && matrix.qt_ver != 6 run: | sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 - - - name: Install Qt (macOS, AppImage & Windows MSVC) + + - name: Install host Qt (Windows MSVC arm64) + if: runner.os == 'Windows' && matrix.architecture == 'arm64' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ matrix.qt_version }} + host: 'windows' + target: 'desktop' + arch: '' + modules: ${{ matrix.qt_modules }} + tools: ${{ matrix.qt_tools }} + cache: ${{ inputs.is_qt_cached }} + cache-key-prefix: host-qt-arm64-windows + dir: ${{ github.workspace }}\HostQt + set-env: false + + - name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC) if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '') uses: jurplel/install-qt-action@v3 with: @@ -201,6 +228,13 @@ jobs: tools: ${{ matrix.qt_tools }} cache: ${{ inputs.is_qt_cached }} + - name: Install MSVC (Windows MSVC) + if: runner.os == 'Windows' && matrix.msystem == '' + uses: ilammy/msvc-dev-cmd@v1 + with: + vsversion: 2022 + arch: ${{ matrix.vcvars_arch }} + - name: Prepare AppImage (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 run: | @@ -210,6 +244,11 @@ jobs: ${{ github.workspace }}/.github/scripts/prepare_JREs.sh + - name: Add QT_HOST_PATH var (Windows MSVC arm64) + if: runner.os == 'Windows' && matrix.architecture == 'arm64' + run: | + echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV + ## # CONFIGURE ## @@ -286,7 +325,7 @@ jobs: ctest -E "^example64|example$" --test-dir build --output-on-failure - name: Test (Windows MSVC) - if: runner.os == 'Windows' && matrix.msystem == '' + if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64' run: | ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }} @@ -324,12 +363,6 @@ jobs: EOF fi - - name: Add VC Enviroment Variables - if: runner.os == 'Windows' && matrix.msystem == '' - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: ${{ matrix.vcvars_arch }} - - name: Package (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' shell: msys2 {0} From efef2e95ab63fa06d0d46506aa3af3e096fb1589 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Thu, 24 Nov 2022 11:53:29 +0000 Subject: [PATCH 179/277] Use ECMQueryQt Only with Qt5 Qt6 provides CMake variables for the paths we need Signed-off-by: TheLastRar --- CMakeLists.txt | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f04b733b..4a80e87b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,11 +235,18 @@ else() message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported") endif() -include(ECMQueryQt) -ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS) -ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS) -ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS) -ecm_query_qt(QT_DATA_DIR QT_HOST_DATA) +if(Launcher_QT_VERSION_MAJOR EQUAL 5) + include(ECMQueryQt) + ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS) + ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS) + ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS) + ecm_query_qt(QT_DATA_DIR QT_HOST_DATA) +else() + set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS}) + set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS}) + set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS}) + set(QT_DATA_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_DATA}) +endif() set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs) # NOTE: Qt 6 already sets this by default From 94d164c0f2a2ab80515351d422cefffbdbf6c910 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Thu, 24 Nov 2022 13:09:17 +0000 Subject: [PATCH 180/277] Remove unused QT_DATA_DIR/QT_MKSPECS_DIR Signed-off-by: TheLastRar --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a80e87b..5c326cfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,14 +240,11 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5) ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS) ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS) ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS) - ecm_query_qt(QT_DATA_DIR QT_HOST_DATA) else() set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS}) set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS}) set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS}) - set(QT_DATA_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_DATA}) endif() -set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs) # NOTE: Qt 6 already sets this by default if (Qt5_POSITION_INDEPENDENT_CODE) From 24193163e102c7421261497b42bb44d22bbf09ef Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Fri, 25 Nov 2022 03:54:15 +0000 Subject: [PATCH 181/277] Maybe fix ResourceFolderModel Clang64 test failure Previously this test would fail sometimes without including ECMQueryQt Signed-off-by: TheLastRar --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c326cfa..2bdd4811 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,7 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE") elseif(Launcher_QT_VERSION_MAJOR EQUAL 6) set(QT_VERSION_MAJOR 6) - find_package(Qt6 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml Core5Compat) + find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat) list(APPEND Launcher_QT_LIBS Qt6::Core5Compat) if(NOT Launcher_FORCE_BUNDLED_LIBS) From 99dd9874df18fc7bea6114466c3c36597cf7d849 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sat, 26 Nov 2022 08:41:41 +0100 Subject: [PATCH 182/277] refactor+fix: refactor the windows part of the release workflow and add arm to it Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 6 ++--- .github/workflows/trigger_release.yml | 36 +++++++++++++++++++-------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d957f627..b18ef30f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,17 +38,17 @@ jobs: qt_tools: '' - os: windows-2022 - name: "Windows-Legacy" + name: "Windows-MinGW-w64-Legacy" msystem: clang32 qt_ver: 5 - os: windows-2022 - name: "Windows" + name: "Windows-MinGW-w64" msystem: clang64 qt_ver: 6 - os: windows-2022 - name: "Windows-Legacy-MSVC" + name: "Windows-MSVC-Legacy" msystem: '' architecture: 'win32' vcvars_arch: 'amd64_x86' diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 8baa9693..ac20c943 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -46,14 +46,27 @@ jobs: tar -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }} - for d in PrismLauncher-Windows-*; do + for d in PrismLauncher-Windows-MSVC*; do + cd "${d}" || continue + LEGACY="$(echo -n ${d} | grep -o Legacy || true)" + ARM64="$(echo -n ${d} | grep -o arm64 || true)" + INST="$(echo -n ${d} | grep -o Setup || true)" + PORT="$(echo -n ${d} | grep -o Portable || true)" + NAME="PrismLauncher-Windows-MSVC" + test -z "${LEGACY}" || NAME="${NAME}-Legacy" + test -z "${ARM64}" || NAME="${NAME}-arm64" + test -z "${PORT}" || NAME="${NAME}-Portable" + test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe + test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" * + cd .. + done + + for d in PrismLauncher-Windows-MinGW-w64*; do cd "${d}" || continue - MSVC="$(echo -n ${d} | grep -o MSVC || true)" LEGACY="$(echo -n ${d} | grep -o Legacy || true)" INST="$(echo -n ${d} | grep -o Setup || true)" PORT="$(echo -n ${d} | grep -o Portable || true)" - NAME="PrismLauncher-Windows" - test -z "${MSVC}" && NAME="${NAME}-MinGW" || NAME="${NAME}-MSVC" + NAME="PrismLauncher-Windows-MinGW-w64" test -z "${LEGACY}" || NAME="${NAME}-Legacy" test -z "${PORT}" || NAME="${NAME}-Portable" test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe @@ -77,15 +90,18 @@ jobs: PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz - PrismLauncher-Windows-MinGW-Legacy-${{ env.VERSION }}.zip - PrismLauncher-Windows-MinGW-Legacy-Portable-${{ env.VERSION }}.zip - PrismLauncher-Windows-MinGW-Legacy-Setup-${{ env.VERSION }}.exe - PrismLauncher-Windows-MinGW-${{ env.VERSION }}.zip - PrismLauncher-Windows-MinGW-Portable-${{ env.VERSION }}.zip - PrismLauncher-Windows-MinGW-Setup-${{ env.VERSION }}.exe + PrismLauncher-Windows-MinGW-w64-Legacy-${{ env.VERSION }}.zip + PrismLauncher-Windows-MinGW-w64-Legacy-Portable-${{ env.VERSION }}.zip + PrismLauncher-Windows-MinGW-w64-Legacy-Setup-${{ env.VERSION }}.exe + PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip + PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip + 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-Portable-${{ env.VERSION }}.zip + PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe From 4c1c26a0e61fa492abb3715fe294fa25ca129649 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 30 Nov 2022 13:52:02 +0100 Subject: [PATCH 183/277] chore: drop Windows-MinGW-w64-Legacy this brings back the number of builds as the same as before the pr this is one of the slowest builds + windows 7/8.1 users can just use Windows-MSVC-Legacy Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 27 ++++++++------------------- .github/workflows/trigger_release.yml | 5 ----- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b18ef30f..1ba5d0e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,15 +37,9 @@ jobs: qt_modules: 'qt5compat qtimageformats' qt_tools: '' - - os: windows-2022 - name: "Windows-MinGW-w64-Legacy" - msystem: clang32 - qt_ver: 5 - - os: windows-2022 name: "Windows-MinGW-w64" msystem: clang64 - qt_ver: 6 - os: windows-2022 name: "Windows-MSVC-Legacy" @@ -135,12 +129,12 @@ jobs: cmake:p extra-cmake-modules:p ninja:p - qt${{ matrix.qt_ver }}-base:p - qt${{ matrix.qt_ver }}-svg:p - qt${{ matrix.qt_ver }}-imageformats:p - quazip-qt${{ matrix.qt_ver }}:p + qt6-base:p + qt6-svg:p + qt6-imageformats:p + quazip-qt6:p ccache:p - ${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }} + qt6-5compat:p - name: Force newer ccache if: runner.os == 'Windows' && matrix.msystem == '' && inputs.build_type == 'Debug' @@ -174,9 +168,9 @@ jobs: uses: actions/cache@v3.0.11 with: path: '${{ github.workspace }}\.ccache' - key: ${{ matrix.os }}-qt${{ matrix.qt_ver }} + key: ${{ matrix.os }}-mingw-w64 restore-keys: | - ${{ matrix.os }}-qt${{ matrix.qt_ver }} + ${{ matrix.os }}-mingw-w64 - name: Set short version shell: bash @@ -267,7 +261,7 @@ jobs: if: runner.os == 'Windows' && matrix.msystem != '' 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 }} -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -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=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja - name: Configure CMake (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' @@ -369,11 +363,6 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} - cd ${{ env.INSTALL_DIR }} - if [ "${{ matrix.qt_ver }}" == "5" ]; then - cp /clang32/bin/libcrypto-1_1.dll /clang32/bin/libssl-1_1.dll ./ - fi - - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index ac20c943..a2f89819 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -63,11 +63,9 @@ jobs: for d in PrismLauncher-Windows-MinGW-w64*; do cd "${d}" || continue - LEGACY="$(echo -n ${d} | grep -o Legacy || true)" INST="$(echo -n ${d} | grep -o Setup || true)" PORT="$(echo -n ${d} | grep -o Portable || true)" NAME="PrismLauncher-Windows-MinGW-w64" - test -z "${LEGACY}" || NAME="${NAME}-Legacy" test -z "${PORT}" || NAME="${NAME}-Portable" test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" * @@ -90,9 +88,6 @@ jobs: PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz - PrismLauncher-Windows-MinGW-w64-Legacy-${{ env.VERSION }}.zip - PrismLauncher-Windows-MinGW-w64-Legacy-Portable-${{ env.VERSION }}.zip - PrismLauncher-Windows-MinGW-w64-Legacy-Setup-${{ env.VERSION }}.exe PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe From a116778402bd98ee079488d8c5a27933369e9046 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 1 Dec 2022 15:15:15 -0300 Subject: [PATCH 184/277] fix(Inst.Import): don't search inside 'overrides/' for the manifest It will never be there anyways, and saves a **bunch** of time when the overrides folder is big and we traverse all the tree when searching for the MMC 'instance.cfg' file. Signed-off-by: flow --- launcher/InstanceImportTask.cpp | 10 +++------- launcher/MMCZip.cpp | 27 ++++++++++++++++----------- launcher/MMCZip.h | 4 +++- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index b490620d..5f459649 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -164,18 +164,14 @@ void InstanceImportTask::processZipPack() } else { - QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg"); - QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json"); + QStringList paths_to_ignore { "overrides/" }; - if (!mmcRoot.isNull()) - { + if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) { // process as MultiMC instance/pack qDebug() << "MultiMC:" << mmcRoot; root = mmcRoot; m_modpackType = ModpackType::MultiMC; - } - else if(!flameRoot.isNull()) - { + } else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) { // process as Flame pack qDebug() << "Flame:" << flameRoot; root = flameRoot; diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 9f4e968f..f6600343 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -39,6 +39,7 @@ #include "MMCZip.h" #include "FileSystem.h" +#include #include // ours @@ -228,23 +229,27 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const } // ours -QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root) +QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) { QuaZipDir rootDir(zip, root); - for(auto fileName: rootDir.entryList(QDir::Files)) - { - if(fileName == what) + for (auto&& fileName : rootDir.entryList(QDir::Files)) { + if (fileName == what) return root; + + QCoreApplication::processEvents(); } - for(auto fileName: rootDir.entryList(QDir::Dirs)) - { - QString result = findFolderOfFileInZip(zip, what, root + fileName); - if(!result.isEmpty()) - { + + // Recurse the search to non-ignored subfolders + for (auto&& fileName : rootDir.entryList(QDir::Dirs)) { + if (ignore_paths.contains(fileName)) + continue; + + QString result = findFolderOfFileInZip(zip, what, ignore_paths, root + fileName); + if (!result.isEmpty()) return result; - } } - return QString(); + + return {}; } // ours diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index ce9775bd..81f9cb90 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -80,9 +80,11 @@ namespace MMCZip /** * Find a single file in archive by file name (not path) * + * \param ignore_paths paths to skip when recursing the search + * * \return the path prefix where the file is */ - QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString("")); + QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QStringList& ignore_paths = {}, const QString &root = QString("")); /** * Find a multiple files of the same name in archive by file name From 79b79b752ce9c22764601da8c4ad7ca77ab8a016 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 1 Dec 2022 15:32:26 -0300 Subject: [PATCH 185/277] fix(Inst.Import): Show error reason in fail dialog Signed-off-by: flow --- launcher/InstanceCreationTask.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/launcher/InstanceCreationTask.cpp b/launcher/InstanceCreationTask.cpp index 3971effa..73dc1789 100644 --- a/launcher/InstanceCreationTask.cpp +++ b/launcher/InstanceCreationTask.cpp @@ -25,9 +25,13 @@ void InstanceCreationTask::executeTask() return; qWarning() << "Instance creation failed!"; - if (!m_error_message.isEmpty()) + if (!m_error_message.isEmpty()) { qWarning() << "Reason: " << m_error_message; - emitFailed(tr("Error while creating new instance.")); + emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message)); + } else { + emitFailed(tr("Error while creating new instance.")); + } + return; } From 386fb28a35e4195838c0711dc894626a5d259c0e Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 1 Dec 2022 15:33:35 -0300 Subject: [PATCH 186/277] fix(FileResolvingTask): Fail instead of crash when there's API errors Also remove non-used variable (rip). Signed-off-by: flow --- .../modplatform/flame/FileResolvingTask.cpp | 20 +++++++++++++++---- .../flame/FlameInstanceCreationTask.cpp | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 25b56fbd..7f1beb1a 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -42,12 +42,25 @@ void Flame::FileResolvingTask::executeTask() void Flame::FileResolvingTask::netJobFinished() { setProgress(1, 3); - int index = 0; // job to check modrinth for blocked projects m_checkJob = new NetJob("Modrinth check", m_network); blockedProjects = QMap(); - auto doc = Json::requireDocument(*result); - auto array = Json::requireArray(doc.object()["data"]); + + QJsonDocument doc; + QJsonArray array; + + try { + doc = Json::requireDocument(*result); + array = Json::requireArray(doc.object()["data"]); + } catch (Json::JsonException& e) { + qCritical() << "Non-JSON data returned from the CF API"; + qCritical() << e.cause(); + + emitFailed(tr("Invalid data returned from the API.")); + + return; + } + for (QJsonValueRef file : array) { auto fileid = Json::requireInteger(Json::requireObject(file)["id"]); auto& out = m_toProcess.files[fileid]; @@ -68,7 +81,6 @@ void Flame::FileResolvingTask::netJobFinished() blockedProjects.insert(&out, output); } } - index++; } connect(m_checkJob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::modrinthCheckFinished); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 91554b58..a00c948a 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -338,6 +338,7 @@ bool FlameCreationTask::createInstance() connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason) { m_mod_id_resolver.reset(); setError(tr("Unable to resolve mod IDs:\n") + reason); + loop.quit(); }); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress); connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); From 3cc987a5b4afdc0b8df5be420fc6d1e2a19fbe66 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 1 Dec 2022 22:54:03 -0300 Subject: [PATCH 187/277] fix: explicitly set scheme for local file in -I argument Otherwise isLocalFile() will return false for local files without the file:// thingy. Signed-off-by: flow --- launcher/ui/MainWindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index e2fb1095..b626bbae 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1817,6 +1817,10 @@ void MainWindow::droppedURLs(QList urls) { // NOTE: This loop only processes one dropped file! for (auto& url : urls) { + // The isLocalFile() check below doesn't work as intended without an explicit scheme. + if (url.scheme().isEmpty()) + url.setScheme("file"); + if (!url.isLocalFile()) { // probably instance/modpack addInstance(url.toString()); break; From 5c6f32507176a0067df8728814ea26d3159e1d61 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 2 Dec 2022 20:47:11 +0100 Subject: [PATCH 188/277] Remove blue-ish tint from remaining flat white icons Signed-off-by: Sefa Eyeoglu --- launcher/resources/flat_white/scalable/shortcut.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/resources/flat_white/scalable/shortcut.svg b/launcher/resources/flat_white/scalable/shortcut.svg index b419a77d..77ccbdd4 100644 --- a/launcher/resources/flat_white/scalable/shortcut.svg +++ b/launcher/resources/flat_white/scalable/shortcut.svg @@ -1,3 +1,3 @@ - + From fa3caf091aca8bc784464e3bdf2fe51a09f7d759 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Sat, 3 Dec 2022 21:54:41 +0800 Subject: [PATCH 189/277] fix: warn before trashing instances Signed-off-by: Ryan Cao <70191398+ryanccn@users.noreply.github.com> --- launcher/ui/MainWindow.cpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index b626bbae..91cc5f29 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -2088,27 +2088,25 @@ void MainWindow::on_actionAbout_triggered() void MainWindow::on_actionDeleteInstance_triggered() { - if (!m_selectedInstance) - { + if (!m_selectedInstance) { return; } auto id = m_selectedInstance->id(); - if (APPLICATION->instances()->trashInstance(id)) { - ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); - return; - } - - auto response = CustomMessageBox::selectable( - this, - tr("CAREFUL!"), - tr("About to delete: %1\nThis is permanent and will completely delete the instance.\n\nAre you sure?").arg(m_selectedInstance->name()), - QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No - )->exec(); - if (response == QMessageBox::Yes) - { + + auto response = + CustomMessageBox::selectable(this, tr("CAREFUL!"), + tr("About to delete: %1\nThis may be permanent and will completely delete the instance.\n\nAre you sure?") + .arg(m_selectedInstance->name()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response == QMessageBox::Yes) { + if (APPLICATION->instances()->trashInstance(id)) { + ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); + return; + } + APPLICATION->instances()->deleteInstance(id); } } From 70620d51374d677bd6cafc21b470446696c9e0a0 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sun, 4 Dec 2022 12:45:36 +0100 Subject: [PATCH 190/277] feat: add a proper server icon Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .../resources/breeze_dark/breeze_dark.qrc | 1 + .../resources/breeze_dark/scalable/server.svg | 13 + .../resources/breeze_light/breeze_light.qrc | 1 + .../breeze_light/scalable/server.svg | 13 + launcher/resources/flat/flat.qrc | 1 + launcher/resources/flat/scalable/server.svg | 1 + launcher/resources/flat_white/flat_white.qrc | 1 + .../resources/flat_white/scalable/server.svg | 1 + launcher/resources/multimc/multimc.qrc | 1 + .../resources/multimc/scalable/server.svg | 9764 +++++++++++++++++ launcher/ui/pages/instance/ServersPage.h | 2 +- 11 files changed, 9798 insertions(+), 1 deletion(-) create mode 100644 launcher/resources/breeze_dark/scalable/server.svg create mode 100644 launcher/resources/breeze_light/scalable/server.svg create mode 100644 launcher/resources/flat/scalable/server.svg create mode 100644 launcher/resources/flat_white/scalable/server.svg create mode 100644 launcher/resources/multimc/scalable/server.svg diff --git a/launcher/resources/breeze_dark/breeze_dark.qrc b/launcher/resources/breeze_dark/breeze_dark.qrc index 97434abc..79707828 100644 --- a/launcher/resources/breeze_dark/breeze_dark.qrc +++ b/launcher/resources/breeze_dark/breeze_dark.qrc @@ -40,5 +40,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/server.svg diff --git a/launcher/resources/breeze_dark/scalable/server.svg b/launcher/resources/breeze_dark/scalable/server.svg new file mode 100644 index 00000000..7d9af3e7 --- /dev/null +++ b/launcher/resources/breeze_dark/scalable/server.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/breeze_light/breeze_light.qrc b/launcher/resources/breeze_light/breeze_light.qrc index 6d868b18..ae8dbf3b 100644 --- a/launcher/resources/breeze_light/breeze_light.qrc +++ b/launcher/resources/breeze_light/breeze_light.qrc @@ -40,5 +40,6 @@ scalable/export.svg scalable/rename.svg scalable/launch.svg + scalable/server.svg diff --git a/launcher/resources/breeze_light/scalable/server.svg b/launcher/resources/breeze_light/scalable/server.svg new file mode 100644 index 00000000..52d7dd7d --- /dev/null +++ b/launcher/resources/breeze_light/scalable/server.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/launcher/resources/flat/flat.qrc b/launcher/resources/flat/flat.qrc index a846bd2d..cadf8736 100644 --- a/launcher/resources/flat/flat.qrc +++ b/launcher/resources/flat/flat.qrc @@ -47,6 +47,7 @@ scalable/tag.svg scalable/export.svg scalable/rename.svg + scalable/server.svg scalable/launch.svg diff --git a/launcher/resources/flat/scalable/server.svg b/launcher/resources/flat/scalable/server.svg new file mode 100644 index 00000000..49c22b38 --- /dev/null +++ b/launcher/resources/flat/scalable/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/flat_white/flat_white.qrc b/launcher/resources/flat_white/flat_white.qrc index b0759d8f..2701462f 100644 --- a/launcher/resources/flat_white/flat_white.qrc +++ b/launcher/resources/flat_white/flat_white.qrc @@ -48,5 +48,6 @@ scalable/rename.svg scalable/tag.svg scalable/launch.svg + scalable/server.svg diff --git a/launcher/resources/flat_white/scalable/server.svg b/launcher/resources/flat_white/scalable/server.svg new file mode 100644 index 00000000..f41db1b2 --- /dev/null +++ b/launcher/resources/flat_white/scalable/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc index 42b496da..9712698d 100644 --- a/launcher/resources/multimc/multimc.qrc +++ b/launcher/resources/multimc/multimc.qrc @@ -346,5 +346,6 @@ scalable/export.svg scalable/launch.svg + scalable/server.svg diff --git a/launcher/resources/multimc/scalable/server.svg b/launcher/resources/multimc/scalable/server.svg new file mode 100644 index 00000000..c6a957b3 --- /dev/null +++ b/launcher/resources/multimc/scalable/server.svg @@ -0,0 +1,9764 @@ + + + + + + + + + + + + + + + + + + + + + + + +image/svg+xml + + + + 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 +byvXOWum0rOluGXuPT4pS3q2kZkyF8/TndQsijF6W5VXE8dqeuN8bT21Xewa1nt+93tv5mKzZL3L +d1vOXTWxeaF9Ts23ir9Ty59fe1OJcjI+tfxSeZhaVguXUwt7HegL+uIumTrKTyW2NltxWstnN7XV +WtxoQZMPfmiTSxcG/e31YBv3JbnayfzCb5etvkegf51coVFUF/J3s1IcxknB3Th1W9Z+7HzkoOZc +L7G1OzWX7qidY1Zsbjd1sz7zDn/u1uHdO4t1/KjTbq937ttP62en8mr6cgG3FVcKtZBqd5XH9O3n +0bJvpU8fxl5gpZr6tToVVOlb+3k1cQO1iNXSSk+MxZmrVv3Ir9LOzLNmBFW6t3WebVx7KkW14GrT +i7eJ9fLpsV+l7d7r2sJSfGPq1a9SuSBvbwRUqs3M5tbf8xjJPn1N3z3KhaJx7tvT6UJrLX5aO77w +rXR3pnnkqZSuF1zt/MFR+jhogK/aj2X1AFW62De8u9M3qXimOw+vpZt9c7o2C7NPqz2bn/fMarqY +3a3jSmE1vdlipU/tp+u3i4BK114yV5WPVbdSKc5V+7xSPAmsNLv6fjnvX+nG1GK7s5bo+Fd6ln2G +Wih++/ramd+6UwIqzXwuzhvVXf9K04tPiY31H66nOEmcO6ulzMyv1jv2q1QuHJ4bAZVqM/FMLrMd +UOndC3IlbBaLvn2d3p3dnDuqfl75Vrp7nrsOGt69uZWp5Cep1H78LgDG+AGeXuzM7yzjAV7qq3Tv +5Sfzu9SSodJsy1vp0f7JE630bmXB01Mpns0kk/dutUJf7w356Oc861/p/nQvd3RXzvlWelqv7rmV +wryI1R4mf1vLAZU+JOTLo9+ef6WHqeejQmF7yq9SmJfifm0/sK+Xx1rxNahSS76Wn/P+lR6tzBQr +L0vruFIp7u3r9fNmJ7DS64XqSyuo0mP5ZmXb8KtUikO1duJmJ9c2fQf4frn4HFjp13TxyAio9FGT +n0qvCVwpwpinrydXte+1+Mmyb6XPz7cvgZU2q9sLH36VSnFU7bH8ah9Z/gNcuFKmb7vn+36Vttsn +KzO00rfUomfRJPJLdgpXKsWV0nR3V6RKa+1eypRRpct9lZ5uLvw+27c7UOlm21NpYq15t0wr/c4v +uZUCTUbVxt8fpslWrhp3yr5IIC5k++RhF1W60r+nnqzMNlLrZ1Cp2fUOr21/JXGlaF4W7GUPKZyv +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 + + +